tinyhumansai/openhuman · warning
missing required field `{key}`
Error message
missing required field `{key}` What it means
The required_string helper (todo.rs:233) could not extract a mandatory field: args[key] is absent or not a JSON string. It backs the per-op mandatory fields of the `todo` tool — add→content, edit→id and content, update_status→id and status, remove→id. The whitespace-only case produces the same message via a separate branch.
Source
Thrown at src/openhuman/agent/tools/todo.rs:233
return BoardLocation::Thread {
workspace_dir: parent.workspace_dir.clone(),
thread_id: ops::ORCHESTRATOR_TASKS_THREAD_ID.to_string(),
};
}
let Some(thread_id) = thread_context::current_thread_id() else {
return BoardLocation::Scratch;
};
BoardLocation::Thread {
workspace_dir: parent.workspace_dir.clone(),
thread_id,
}
}
fn required_string(args: &serde_json::Value, key: &str) -> anyhow::Result<String> {
let value = args
.get(key)
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("missing required field `{key}`"))?;
let trimmed = value.trim();
if trimmed.is_empty() {
return Err(anyhow::anyhow!("missing required field `{key}`"));
}
Ok(trimmed.to_string())
}
fn optional_string(args: &serde_json::Value, key: &str) -> Option<String> {
args.get(key)
.and_then(|v| v.as_str())
.map(|s| s.to_string())
}
fn patch_from_args(args: &serde_json::Value) -> anyhow::Result<CardPatch> {
let status: Option<TaskCardStatus> = match args.get("status").and_then(|v| v.as_str()) {
Some(s) => Some(ops::parse_status(s).map_err(anyhow::Error::msg)?),
None => None,
};View on GitHub (pinned to a221052e0d)
Solutions
- Include the field the {key} names, as a non-empty string
- Check exact key spelling per op (content, id, status)
- Validate op-specific required fields before dispatch in wrappers
Example fix
// before
{ "op": "update_status", "status": "done" }
// after
{ "op": "update_status", "id": "t1", "status": "done" } Defensive patterns
Strategy: validation
Validate before calling
const REQUIRED_PER_OP: Record<string, string[]> = {
add: ["content"], edit: ["id", "content"],
update_status: ["id", "status"], remove: ["id"],
};
function missingFields(op: string, a: Record<string, unknown>): string[] {
return (REQUIRED_PER_OP[op] ?? []).filter(k => typeof a[k] !== "string");
} Type guard
function hasRequiredStrings(a: unknown, keys: string[]): boolean {
const v = a as Record<string, unknown>;
return keys.every(k => typeof v?.[k] === "string" && (v[k] as string).trim() !== "");
} Try / catch
if e.to_string().contains("missing required field") {
return Ok(ToolResult::error(format!("{e}; per-op required: add=content, edit=id+content, update_status=id+status, remove=id")));
} Prevention
- Check per-op required fields before dispatch — the helper enforces both presence and non-blank content
- Use exact key spellings (content, id, status)
- Never assume context carries the id — pass it explicitly
When it happens
Trigger: add without content; edit without id or content; update_status without id or status; remove without id; any of these passed as a non-string JSON value; key-name drift (text instead of content, task_id instead of id).
Common situations: Models assuming the id from context; argument builders that omit fields they consider implicit.
Related errors
- missing required field `op`
- missing `cards` for op=replace
- invalid approvalMode '{other}' (expected required|not_requir
- invalid approvalMode type (expected required|not_required|nu
- Missing 'agent' parameter
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/35bd73f5f666d940.
Report an issue: GitHub.