{"record":{"id":"b8e8383e518fc89b","repo":"tinyhumansai/openhuman","slug":"invalid-approvalmode-other-expected-required","errorCode":null,"errorMessage":"invalid approvalMode '{other}' (expected required|not_required|null)","messagePattern":"invalid approvalMode '(.+?)' \\(expected required\\|not_required\\|null\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/openhuman/agent/tools/todo.rs","lineNumber":258,"sourceCode":"\nfn optional_string(args: &serde_json::Value, key: &str) -> Option<String> {\n    args.get(key)\n        .and_then(|v| v.as_str())\n        .map(|s| s.to_string())\n}\n\nfn patch_from_args(args: &serde_json::Value) -> anyhow::Result<CardPatch> {\n    let status: Option<TaskCardStatus> = match args.get(\"status\").and_then(|v| v.as_str()) {\n        Some(s) => Some(ops::parse_status(s).map_err(anyhow::Error::msg)?),\n        None => None,\n    };\n    let approval_mode = match args.get(\"approvalMode\") {\n        Some(value) if value.is_null() => Some(None),\n        Some(value) => match value.as_str() {\n            Some(\"required\") => Some(Some(TaskApprovalMode::Required)),\n            Some(\"not_required\") => Some(Some(TaskApprovalMode::NotRequired)),\n            Some(other) => {\n                return Err(anyhow::anyhow!(\n                    \"invalid approvalMode '{other}' (expected required|not_required|null)\"\n                ))\n            }\n            None => {\n                return Err(anyhow::anyhow!(\n                    \"invalid approvalMode type (expected required|not_required|null)\"\n                ))\n            }\n        },\n        None => None,\n    };\n    Ok(CardPatch {\n        content: None,\n        status,\n        objective: optional_string(args, \"objective\"),\n        plan: optional_string_array(args, \"plan\")?,\n        assigned_agent: optional_string(args, \"assignedAgent\"),\n        allowed_tools: optional_string_array(args, \"allowedTools\")?,","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/tools/todo.rs#L240-L276","documentation":"CardPatch parsing: args[\"approvalMode\"] is a string but not one of the three accepted literals (todo.rs:258). Matching is case-sensitive: exactly \"required\", \"not_required\", or JSON null (null clears the override). Any other spelling — including camelCase variants — is rejected.","triggerScenarios":"Passing \"ApprovalMode\", \"notRequired\", \"none\", \"auto\", or \"REQUIRED\"; model-generated camelCase mirroring the JSON key name instead of the value literals.","commonSituations":"Models echoing the key casing for the value; wrappers forwarding UI enum names that differ from the wire literals.","solutions":["Use exactly \"required\" or \"not_required\" (lowercase, snake_case), or omit the field to leave it unchanged","Use JSON null to explicitly clear an existing approval-mode override","Map UI/enum names to the wire literals in wrappers before invoking"],"exampleFix":"// before\n{ \"op\": \"edit\", \"id\": \"t1\", \"approvalMode\": \"notRequired\" }\n\n// after\n{ \"op\": \"edit\", \"id\": \"t1\", \"approvalMode\": \"not_required\" }","handlingStrategy":"type-guard","validationCode":"const OK = new Set([\"required\", \"not_required\"]);\nconst a = args as Record<string, unknown>;\nif (\"approvalMode\" in a && a.approvalMode !== null\n    && (typeof a.approvalMode !== \"string\" || !OK.has(a.approvalMode))) {\n  throw new Error(\"approvalMode must be 'required' | 'not_required' | null\");\n}","typeGuard":"function isApprovalMode(v: unknown): v is \"required\" | \"not_required\" | null {\n  return v === null || v === \"required\" || v === \"not_required\";\n}","tryCatchPattern":"if e.to_string().contains(\"invalid approvalMode\") {\n    return Ok(ToolResult::error(\"approvalMode accepts exactly 'required', 'not_required', or null (case-sensitive)\"));\n}","preventionTips":["Use the exact lowercase snake_case literals — case-sensitive matching","Map UI enum names to wire literals in wrappers","Use null to clear an override, omit the field to leave it unchanged"],"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"}