{"record":{"id":"d4a63adc1147447a","repo":"tinyhumansai/openhuman","slug":"agentdefinitionregistry-missing-after-init","errorCode":null,"errorMessage":"AgentDefinitionRegistry missing after init","messagePattern":"AgentDefinitionRegistry missing after init","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/debug/mod.rs","lineNumber":150,"sourceCode":"/// per currently-connected Composio toolkit — if the user has gmail +\n/// notion connected, `dump_all_agent_prompts` returns an entry for\n/// `integrations_agent@gmail` and another for `integrations_agent@notion`.\n/// When no toolkit is connected, `integrations_agent` is omitted\n/// entirely (there's nothing meaningful to render).\n///\n/// Order follows [`AgentDefinitionRegistry::list`], with\n/// `integrations_agent` replaced in place by its per-toolkit expansion.\npub async fn dump_all_agent_prompts(\n    workspace_dir_override: Option<PathBuf>,\n    model_override: Option<String>,\n) -> Result<Vec<DumpedPrompt>> {\n    let config = load_dump_config(workspace_dir_override, model_override).await?;\n\n    AgentDefinitionRegistry::init_global(&config.workspace_dir)\n        .context(\"initialising AgentDefinitionRegistry for prompt dump\")?;\n\n    let registry = AgentDefinitionRegistry::global()\n        .ok_or_else(|| anyhow!(\"AgentDefinitionRegistry missing after init\"))?;\n\n    let ids: Vec<String> = registry\n        .list()\n        .iter()\n        .filter(|d| d.id != \"fork\")\n        .map(|d| d.id.clone())\n        .collect();\n\n    let mut results = Vec::with_capacity(ids.len());\n    for id in ids {\n        if id == INTEGRATIONS_AGENT_ID {\n            let toolkits = connected_toolkits_for(&config).await?;\n            if toolkits.is_empty() {\n                log::info!(\"[agent::debug] skipping integrations_agent — no connected toolkits\");\n                continue;\n            }\n            for toolkit in toolkits {\n                let dumped = render_integrations_agent(&config, &toolkit)","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/debug/mod.rs#L132-L168","documentation":"Defensive invariant in dump_all_agent_prompts (src/openhuman/agent/debug/mod.rs:150): `AgentDefinitionRegistry::init_global(&workspace)` returned Ok but a subsequent `AgentDefinitionRegistry::global()` yielded None. Under normal sequencing this cannot happen (init_global installs the global before returning), so firing it indicates the global was cleared/replaced concurrently or init_global's Ok concealed a no-op on a mismatched path.","triggerScenarios":"Another thread/task calling a registry reset or re-init between the two statements; embedding code that tears down global singletons mid-dump; test harnesses that swap globals between #[tokio::test] runs sharing a process.","commonSituations":"Long-running debug tooling that dumps prompts while the core reloads agent definitions (workspace TOML change triggers reload); parallel test execution mutating process globals; custom hosts invoking init_global with different workspace dirs concurrently.","solutions":["Retry the dump once — a transient global swap resolves after reload settles.","Serialize: avoid mutating/re-initializing the registry while a prompt dump runs.","In custom hosts, call AgentDefinitionRegistry::init_global once at startup (as factory.rs's sibling message instructs) rather than re-init racing other users.","If reproducible single-threaded, capture the workspace_dir used and check for two different values across init sites."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match AgentDefinitionRegistry::global() {\n    Some(reg) => reg,\n    None => {\n        // init_global returned Ok but global is gone: concurrent reset.\n        // Re-init once, serialize future access, then retry the dump.\n        AgentDefinitionRegistry::init_global(&config.workspace_dir)?;\n        AgentDefinitionRegistry::global().ok_or_else(|| anyhow!(\"registry missing after re-init\"))?\n    }\n}","preventionTips":["Initialize the registry once at startup and never re-init while readers run.","Serialize registry reloads (workspace TOML changes) against prompt dumps.","In tests, isolate registry global mutations per process."],"tags":["rust","registry","concurrency","invariant","agent-prompts","debug"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}