{"record":{"id":"9ad79b00ba779ccc","repo":"tinyhumansai/openhuman","slug":"missing-cards-for-op-replace","errorCode":null,"errorMessage":"missing `cards` for op=replace","messagePattern":"missing `cards` for op=replace","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/openhuman/agent/tools/todo.rs","lineNumber":156,"sourceCode":"                let id = required_string(&args, \"id\")?;\n                let mut patch = patch_from_args(&args)?;\n                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,","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/tools/todo.rs#L138-L174","documentation":"The `todo` tool dispatched op=replace but args has no `cards` key (todo.rs:156). replace swaps the entire board for the provided card list, so cards is mandatory for that op — clearing the board is a separate op=clear, not an empty replace.","triggerScenarios":"Model sends { \"op\": \"replace\" } with no cards; wrapper mapping a board update to replace without including the new card array; intending to clear but choosing the wrong op.","commonSituations":"Board-sync logic that falls back to replace without a payload; models conflating replace and clear.","solutions":["Include \"cards\" as a JSON array of card objects for op=replace","Use op=clear when the intent is an empty board","Validate that op=replace calls carry an array cards field before dispatch"],"exampleFix":"// before\n{ \"op\": \"replace\" }\n\n// after\n{ \"op\": \"replace\", \"cards\": [{ \"id\": \"t1\", \"content\": \"Ship the fix\" }] }","handlingStrategy":"validation","validationCode":"const a = args as Record<string, unknown>;\nif (a?.op === \"replace\" && !Array.isArray(a.cards)) {\n  throw new Error(\"todo replace: 'cards' array is required (use op=clear to empty the board)\");\n}","typeGuard":"function isReplaceArgs(a: unknown): a is { op: \"replace\"; cards: unknown[] } {\n  const v = a as Record<string, unknown>;\n  return v?.op === \"replace\" && Array.isArray(v.cards);\n}","tryCatchPattern":"if e.to_string().contains(\"missing `cards` for op=replace\") {\n    return Ok(ToolResult::error(\"replace needs the full 'cards' array; use op=clear for an empty board\"));\n}","preventionTips":["Remember replace carries the complete board, clear empties it","Validate replace calls carry an array cards field in wrappers","When syncing board state, build the cards array before choosing replace"],"tags":["rust","tool","arguments","llm","todo"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}