Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 31 additions & 0 deletions cargo-near-build/src/cargo_native/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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? {
Expand All @@ -156,6 +157,13 @@ where
}
Message::CompilerMessage(message) => {
if let Some(msg) = message.message.rendered {
// 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;
}
for line in msg.lines() {
eprintln!(" │ {line}");
}
Expand All @@ -165,6 +173,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 || {
Expand Down
75 changes: 75 additions & 0 deletions integration-tests/tests/abi/json_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 `Item: 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<Item> {
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(())
}
Loading