Hmbown/CodeWhale · error
.annotations is not a valid MCP annotations object
Error message
{context}.annotations is not a valid MCP annotations object What it means
parse_resource_entry validates the optional 'annotations' field of an MCP resource entry against valid_annotations (audience, priority range, lastModifiedDateTime shape). If the object does not conform to the MCP annotations schema, the entry is rejected with this error instead of passing invalid metadata into the catalog.
Solutions
- Fix the server to emit annotations conforming to the MCP spec (audience array of Role, priority in 0.0..1.0)
- Drop the 'annotations' field if it cannot be made valid — it is optional
- Compare the emitted object against the validator (valid_annotations) to pinpoint the non-conforming key
Example fix
// before
"annotations":{"priority":5}
// after
"annotations":{"audience":["user"],"priority":0.5} Defensive patterns
Strategy: validation
Validate before calling
fn has_valid_annotations(v: &Value) -> bool {
v.get("annotations").map_or(true, |a| {
a.is_object()
&& a.get("priority").map_or(true, |p| p.as_f64().map_or(false, |f| (0.0..=1.0).contains(&f)))
&& a.get("audience").map_or(true, |x| x.is_array())
})
} Type guard
fn annotations_are_valid(a: &serde_json::Value) -> bool {
a.is_object()
&& a.get("audience").map_or(true, |x| x.as_array().map_or(false, |arr| arr.iter().all(|r| r == "user" || r == "assistant")))
&& a.get("priority").map_or(true, |p| p.as_f64().map_or(false, |f| (0.0..=1.0).contains(&f)))
} Try / catch
match client.list_resources_with_metadata().await {
Ok(entries) => use(entries),
Err(e) if e.to_string().contains("annotations is not a valid MCP annotations object") => {
log::warn("malformed annotations; treating entry as unannotated");
}
Err(e) => return Err(e),
} Prevention
- Mirror the MCP annotations schema (audience, priority 0.0-1.0) in server output
- Add schema tests for annotation payloads
- Omit annotations rather than emitting a partial object
When it happens
Trigger: list_resources_with_metadata parses a resources/list entry whose 'annotations' is present but fails valid_annotations — wrong types, missing/invalid audience, priority outside 0.0..=1.0, or not an object at all.
Common situations: MCP server bug emitting malformed annotations; manually crafted server responses; newer annotations fields not covered by the validator after a server upgrade.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- .size must be an integer
- invalid JSON payload at key
- bad_target
- Burn rate is optional. When set, it must be a positive $/hr.
- Cargo metadata dependencies for
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/baef82b7b5b4209f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/mcp/src/stdio_client.rs:205
.and_then(Value::as_str)
.with_context(|| format!("{context}.name must be a string"))?
.to_string();
let description = optional_string_field(fields, "description", &context)?;
let mime_type = optional_string_field(fields, "mimeType", &context)?;
let mut metadata = json!({"name": name});
if let Some(mime_type) = mime_type {
metadata["mimeType"] = Value::String(mime_type);
}
if let Some(size) = fields.get("size") {
if size.as_i64().is_none() && size.as_u64().is_none() {
bail!("{context}.size must be an integer");
}
metadata["size"] = size.clone();
}
if let Some(annotations) = fields.get("annotations") {
if !valid_annotations(annotations) {
bail!("{context}.annotations is not a valid MCP annotations object");
}
metadata["annotations"] = annotations.clone();
}
Ok((
McpResourceDescriptor {
server_name: server_name.to_string(),
uri,
description,
},
metadata,
))
}
/// Read one newline-delimited child message without ever retaining more than
/// `max_bytes`. On an oversized line the reader is intentionally abandoned;
/// continuing after losing JSON-RPC framing would be unsafe.
pub(crate) fn read_bounded_line<R: BufRead>(View on GitHub (pinned to 73e0f67d83)