{"record":{"id":"c9539f989869a7e7","repo":"tinyhumansai/openhuman","slug":"agent-id-not-found","errorCode":null,"errorMessage":"agent_id '{}' not found","messagePattern":"agent_id '(.+?)' not found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/orchestration/tools/spawn_worker_thread.rs","lineNumber":194,"sourceCode":"            if is_delegated_label || current_thread.parent_thread_id.is_some() {\n                tracing::warn!(\n                    agent_id = %agent_id,\n                    current_thread_id = %current_thread_id,\n                    is_delegated_label,\n                    has_parent_thread_id = current_thread.parent_thread_id.is_some(),\n                    elapsed_ms = started.elapsed().as_millis() as u64,\n                    \"[spawn_worker_thread] depth guard blocked spawn from worker thread\"\n                );\n                return Ok(ToolResult::error(\"Worker threads cannot spawn other worker threads. Depth is capped at 1. Use spawn_subagent for inline delegation instead.\"));\n            }\n        }\n\n        let registry = AgentDefinitionRegistry::global()\n            .ok_or_else(|| anyhow::anyhow!(\"AgentDefinitionRegistry not initialised\"))?;\n\n        let definition = registry\n            .get(&agent_id)\n            .ok_or_else(|| anyhow::anyhow!(\"agent_id '{}' not found\", agent_id))?;\n\n        if !parent.allowed_subagent_ids.contains(&definition.id) {\n            tracing::warn!(\n                parent_agent = %parent.agent_definition_id,\n                requested_agent = %definition.id,\n                allowed = ?parent.allowed_subagent_ids,\n                \"[spawn_worker_thread] blocked subagent outside parent allowlist\"\n            );\n            return Ok(ToolResult::error(format!(\n                \"spawn_worker_thread: agent '{}' is not in parent agent '{}' subagents.allowlist\",\n                definition.id, parent.agent_definition_id\n            )));\n        }\n\n        tracing::debug!(\n            parent_agent = %parent.agent_definition_id,\n            requested_agent = %definition.id,\n            \"[spawn_worker_thread] subagent allowlist check passed\"","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/orchestration/tools/spawn_worker_thread.rs#L176-L212","documentation":"Thrown by the spawn_worker_thread tool when the requested agent_id is not present in the global AgentDefinitionRegistry. The registry holds builtin agent definitions plus any loaded custom ones; the lookup happens after the depth guard and before the parent allowlist check, so an allowlisted-but-unregistered id fails here while a registered-but-unallowlisted id fails the next check instead.","triggerScenarios":"Invoking the spawn_worker_thread tool with an agent_id that is not registered: a typo, a custom agent TOML that was never loaded, a builtin that is compiled out by a feature gate (e.g. skill_setup/skill_executor without the skills feature), or a definition removed between listing and spawn.","commonSituations":"Agent definition file not in the scanned agents directory or failing to parse; running a slim build where the agent's feature gate is off; renaming an agent id without updating prompts/tools that reference it.","solutions":["List available agent definitions (registry / subagents surface) and use an exact id","If custom: verify the agent definition file loads (correct dir, valid TOML/frontmatter) and re-check after startup","If a builtin is missing, confirm the binary was built with the feature that registers it (e.g. skills)"],"exampleFix":"// before\nspawn_worker_thread(agent_id = \"reseacher\") // typo, not registered\n\n// after — resolve from the registry before invoking the tool\nlet registry = AgentDefinitionRegistry::global()\n    .ok_or_else(|| anyhow!(\"AgentDefinitionRegistry not initialised\"))?;\nlet def = registry.get(&agent_id)\n    .with_context(|| format!(\"agent_id '{agent_id}' not found; known: {:?}\", registry.ids()))?;\n// then spawn def.id","handlingStrategy":"validation","validationCode":"let registry = AgentDefinitionRegistry::global()\n    .ok_or_else(|| anyhow!(\"AgentDefinitionRegistry not initialised\"))?;\nif registry.get(&agent_id).is_none() {\n    // list registered ids, surface them, and stop before invoking spawn_worker_thread\n}","typeGuard":"fn agent_registered(agent_id: &str) -> bool {\n    AgentDefinitionRegistry::global().map_or(false, |r| r.get(agent_id).is_some())\n}","tryCatchPattern":"match spawn_worker_thread(args).await {\n    Ok(res) => Ok(res),\n    Err(e) if e.to_string().contains(\"not found\") => {\n        // surface available agent ids to the caller/model so it can pick a valid one\n        list_available_agents_and_retry(args)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Enumerate registry ids at prompt-build time and give the model only valid agent ids to choose from","Verify custom agent definitions actually load at startup (dir + parse) before referencing them","Remember allowlist is a separate, later check — passing the registry check does not guarantee the spawn is allowed"],"tags":["agents","registry","tool-call","spawn"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}