{"record":{"id":"60929e4d5d509d14","repo":"tinyhumansai/openhuman","slug":"invalid-cards-e","errorCode":null,"errorMessage":"invalid `cards`: {e}","messagePattern":"invalid `cards`: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/openhuman/agent/tools/todo.rs","lineNumber":158,"sourceCode":"                patch.content = optional_string(&args, \"content\");\n                ops::edit(&location, &id, patch).await\n            }\n            \"update_status\" => {\n                let id = required_string(&args, \"id\")?;\n                let status = required_string(&args, \"status\")?;\n                let status = ops::parse_status(&status).map_err(anyhow::Error::msg)?;\n                ops::update_status(&location, &id, status).await\n            }\n            \"remove\" => {\n                let id = required_string(&args, \"id\")?;\n                ops::remove(&location, &id).await\n            }\n            \"replace\" => {\n                let cards = args\n                    .get(\"cards\")\n                    .ok_or_else(|| anyhow::anyhow!(\"missing `cards` for op=replace\"))?;\n                let cards: Vec<TaskBoardCard> = serde_json::from_value(cards.clone())\n                    .map_err(|e| anyhow::anyhow!(\"invalid `cards`: {e}\"))?;\n                ops::replace(&location, cards).await\n            }\n            \"clear\" => ops::clear(&location).await,\n            \"list\" => ops::list(&location).await,\n            other => {\n                return Ok(ToolResult::error(format!(\n                \"unknown op '{other}' (expected add|edit|update_status|remove|replace|clear|list)\"\n            )))\n            }\n        };\n\n        match result {\n            Ok(snap) => {\n                let payload = json!({\n                    \"threadId\": snap.thread_id,\n                    \"cards\": snap.cards,\n                    \"markdown\": snap.markdown,\n                });","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/tools/todo.rs#L140-L176","documentation":"op=replace found `cards` but serde_json could not deserialize it into Vec<TaskBoardCard> (todo.rs:158). The {e} chain names the exact mismatch: cards is not a JSON array, or an element is missing a required card field or has a wrong type. Distinct from the missing-cards error, which fires only when the key is absent.","triggerScenarios":"cards passed as an object or map keyed by id instead of an array; card elements missing required fields (e.g. id/content) or carrying wrong JSON types (numbers for string fields); null cards (deserialization failure, not the missing-key path).","commonSituations":"Models echoing the board shape from memory instead of the schema; wrappers forwarding a UI state object whose card fields were renamed.","solutions":["Make cards a JSON array of card objects matching TaskBoardCard's required fields and types","Read the {e} message — serde names the offending field and expected type precisely","Round-trip validate with serde_json::from_value::<Vec<TaskBoardCard>> in wrappers before invoking the tool"],"exampleFix":"// before\n{ \"op\": \"replace\", \"cards\": { \"t1\": { \"content\": \"Ship\" } } }\n\n// after\n{ \"op\": \"replace\", \"cards\": [{ \"id\": \"t1\", \"content\": \"Ship the fix\" }] }","handlingStrategy":"validation","validationCode":"// Rust — pre-flight the exact deserialization the tool will perform\nlet cards: Vec<TaskBoardCard> = serde_json::from_value(args[\"cards\"].clone())\n    .context(\"cards payload does not match TaskBoardCard shape\")?;","typeGuard":"function isCardArray(v: unknown): v is Array<Record<string, unknown>> {\n  return Array.isArray(v) && v.every(c => typeof c === \"object\" && c !== null && \"id\" in c);\n}","tryCatchPattern":"// Surface serde's precise field error to the model for self-correction\nif e.to_string().contains(\"invalid `cards`\") {\n    return Ok(ToolResult::error(format!(\"cards shape invalid: {e:#}} — see card schema\")));\n}","preventionTips":["Send cards as a flat array of card objects with all required string fields","Echo card objects from a prior list op rather than reconstructing from memory","Round-trip validate with from_value before invoking the tool"],"tags":["rust","tool","arguments","serde","todo"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}