From 1e0586d3db0d86648533d17d3e2036b1443c9b52 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 00:05:35 +0000 Subject: [PATCH 1/4] Initial plan From 13f7a1b117ae527aec051904e574f39280dcdd7a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 00:17:27 +0000 Subject: [PATCH 2/4] Add improved error handling for missing JsonSchema trait and test Co-authored-by: r-near <163825889+r-near@users.noreply.github.com> --- cargo-near-build/src/cargo_native/compile.rs | 28 ++++++++ integration-tests/tests/abi/json_schema.rs | 75 ++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/cargo-near-build/src/cargo_native/compile.rs b/cargo-near-build/src/cargo_native/compile.rs index 1a2e891c..9c383d2f 100644 --- a/cargo-near-build/src/cargo_native/compile.rs +++ b/cargo-near-build/src/cargo_native/compile.rs @@ -148,6 +148,7 @@ where // stdout and stderr have to be processed concurrently to not block the process from progressing let thread_stdout = thread::spawn(move || -> eyre::Result<_, std::io::Error> { let mut artifacts = vec![]; + let mut has_json_schema_error = false; let stdout_reader = std::io::BufReader::new(child_stdout); for message in Message::parse_stream(stdout_reader) { match message? { @@ -156,6 +157,10 @@ where } Message::CompilerMessage(message) => { if let Some(msg) = message.message.rendered { + // Check if this is a JsonSchema-related error + if msg.contains("the trait bound") && msg.contains("JsonSchema") && msg.contains("is not satisfied") { + has_json_schema_error = true; + } for line in msg.lines() { eprintln!(" │ {line}"); } @@ -165,6 +170,29 @@ where }; } + if has_json_schema_error { + eprintln!(" │"); + eprintln!(" │ ╭─────────────────────────────────────────────────────────────────╮"); + eprintln!(" │ │ HINT: The error above indicates that a type used in your │"); + eprintln!(" │ │ contract's public interface does not implement the JsonSchema │"); + eprintln!(" │ │ trait, which is required for ABI generation. │"); + eprintln!(" │ │ │"); + eprintln!(" │ │ To fix this, add the JsonSchema derive to your struct/enum: │"); + eprintln!(" │ │ │"); + eprintln!(" │ │ Option 1 (Recommended - works with near-sdk 5.0+): │"); + eprintln!(" │ │ #[near(serializers = [json])] │"); + eprintln!(" │ │ pub struct YourType {{ ... }} │"); + eprintln!(" │ │ │"); + eprintln!(" │ │ Option 2 (Manual - for more control): │"); + eprintln!(" │ │ use schemars::JsonSchema; │"); + eprintln!(" │ │ #[derive(Serialize, Deserialize, JsonSchema)] │"); + eprintln!(" │ │ pub struct YourType {{ ... }} │"); + eprintln!(" │ │ │"); + eprintln!(" │ │ For more information, see: │"); + eprintln!(" │ │ https://github.com/near/near-sdk-rs │"); + eprintln!(" │ ╰─────────────────────────────────────────────────────────────────╯"); + } + Ok(artifacts) }); let thread_stderr = thread::spawn(move || { diff --git a/integration-tests/tests/abi/json_schema.rs b/integration-tests/tests/abi/json_schema.rs index f7f43c8e..67cc3b91 100644 --- a/integration-tests/tests/abi/json_schema.rs +++ b/integration-tests/tests/abi/json_schema.rs @@ -701,3 +701,78 @@ fn test_schema_complex() -> testresult::TestResult { Ok(()) } + +#[test] +#[named] +fn test_schema_vec_of_custom_types() -> testresult::TestResult { + // This test demonstrates the proper way to define custom types that can be used + // in Vec return types. Without #[near(serializers = [json])], the ABI generation + // would fail with "the trait bound `CustomType: JsonSchema` is not satisfied" + let abi_root = generate_abi! { + use near_sdk::near; + + #[near(serializers = [json])] + pub struct Item { + pub id: u32, + pub name: String, + } + + #[near(contract_state)] + #[derive(Default)] + pub struct Contract {} + + #[near] + impl Contract { + pub fn get_items(&self) -> Vec { + vec![] + } + } + }; + + assert_eq!(abi_root.body.functions.len(), 2); + let function = &abi_root.body.functions[1]; + assert_eq!(function.name, "get_items"); + + // Verify the return type is an array of Item references + let return_schema = function.result.as_ref().unwrap().json_schema(); + let expected_schema: Schema = serde_json::from_str( + r#" + { + "type": "array", + "items": { + "$ref": "#/definitions/Item" + } + } + "#, + )?; + assert_eq!(return_schema.type_schema, expected_schema); + + // Verify the Item definition is in the schema definitions + let item_schema: Schema = serde_json::from_str( + r#" + { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "type": "string" + } + } + } + "#, + )?; + assert_eq!( + abi_root.body.root_schema.definitions["Item"], + item_schema + ); + + Ok(()) +} From 19fc81acbab0f8dedaa153ca768ca99bdf2b04e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 00:27:07 +0000 Subject: [PATCH 3/4] Add CHANGELOG entry for improved JsonSchema error messages Co-authored-by: r-near <163825889+r-near@users.noreply.github.com> --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4234217b..b6c0d80c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Improved error messages when ABI generation fails due to missing `JsonSchema` trait implementation on custom types used in contract functions + ## [0.19.0](https://github.com/near/cargo-near/compare/cargo-near-v0.18.0...cargo-near-v0.19.0) - 2026-01-12 ### Added From 11c6ba5bd23048f0223e5f41dc37fec967be4f04 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 00:28:40 +0000 Subject: [PATCH 4/4] Address code review feedback: improve comments and consistency Co-authored-by: r-near <163825889+r-near@users.noreply.github.com> --- cargo-near-build/src/cargo_native/compile.rs | 5 ++++- integration-tests/tests/abi/json_schema.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cargo-near-build/src/cargo_native/compile.rs b/cargo-near-build/src/cargo_native/compile.rs index 9c383d2f..1673a590 100644 --- a/cargo-near-build/src/cargo_native/compile.rs +++ b/cargo-near-build/src/cargo_native/compile.rs @@ -157,7 +157,10 @@ where } Message::CompilerMessage(message) => { if let Some(msg) = message.message.rendered { - // Check if this is a JsonSchema-related error + // Check if this is a JsonSchema-related error. + // We use simple string matching as the error format is stable across rustc versions + // and this provides a good user experience without complex parsing. + // Example error: "the trait bound `Attestation: JsonSchema` is not satisfied" if msg.contains("the trait bound") && msg.contains("JsonSchema") && msg.contains("is not satisfied") { has_json_schema_error = true; } diff --git a/integration-tests/tests/abi/json_schema.rs b/integration-tests/tests/abi/json_schema.rs index 67cc3b91..2d9385f4 100644 --- a/integration-tests/tests/abi/json_schema.rs +++ b/integration-tests/tests/abi/json_schema.rs @@ -707,7 +707,7 @@ fn test_schema_complex() -> testresult::TestResult { fn test_schema_vec_of_custom_types() -> testresult::TestResult { // This test demonstrates the proper way to define custom types that can be used // in Vec return types. Without #[near(serializers = [json])], the ABI generation - // would fail with "the trait bound `CustomType: JsonSchema` is not satisfied" + // would fail with "the trait bound `Item: JsonSchema` is not satisfied" let abi_root = generate_abi! { use near_sdk::near;