tinyhumansai/openhuman · error · anyhow::Error

AgentDefinitionRegistry missing after init

Error message

AgentDefinitionRegistry missing after init

What it means

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.

Source

Thrown at src/openhuman/agent/debug/mod.rs:150

/// per currently-connected Composio toolkit — if the user has gmail +
/// notion connected, `dump_all_agent_prompts` returns an entry for
/// `integrations_agent@gmail` and another for `integrations_agent@notion`.
/// When no toolkit is connected, `integrations_agent` is omitted
/// entirely (there's nothing meaningful to render).
///
/// Order follows [`AgentDefinitionRegistry::list`], with
/// `integrations_agent` replaced in place by its per-toolkit expansion.
pub async fn dump_all_agent_prompts(
    workspace_dir_override: Option<PathBuf>,
    model_override: Option<String>,
) -> Result<Vec<DumpedPrompt>> {
    let config = load_dump_config(workspace_dir_override, model_override).await?;

    AgentDefinitionRegistry::init_global(&config.workspace_dir)
        .context("initialising AgentDefinitionRegistry for prompt dump")?;

    let registry = AgentDefinitionRegistry::global()
        .ok_or_else(|| anyhow!("AgentDefinitionRegistry missing after init"))?;

    let ids: Vec<String> = registry
        .list()
        .iter()
        .filter(|d| d.id != "fork")
        .map(|d| d.id.clone())
        .collect();

    let mut results = Vec::with_capacity(ids.len());
    for id in ids {
        if id == INTEGRATIONS_AGENT_ID {
            let toolkits = connected_toolkits_for(&config).await?;
            if toolkits.is_empty() {
                log::info!("[agent::debug] skipping integrations_agent — no connected toolkits");
                continue;
            }
            for toolkit in toolkits {
                let dumped = render_integrations_agent(&config, &toolkit)

View on GitHub (pinned to a221052e0d)

Solutions

  1. Retry the dump once — a transient global swap resolves after reload settles.
  2. Serialize: avoid mutating/re-initializing the registry while a prompt dump runs.
  3. In custom hosts, call AgentDefinitionRegistry::init_global once at startup (as factory.rs's sibling message instructs) rather than re-init racing other users.
  4. If reproducible single-threaded, capture the workspace_dir used and check for two different values across init sites.
Defensive patterns

Strategy: try-catch

Try / catch

match AgentDefinitionRegistry::global() {
    Some(reg) => reg,
    None => {
        // init_global returned Ok but global is gone: concurrent reset.
        // Re-init once, serialize future access, then retry the dump.
        AgentDefinitionRegistry::init_global(&config.workspace_dir)?;
        AgentDefinitionRegistry::global().ok_or_else(|| anyhow!("registry missing after re-init"))?
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/d4a63adc1147447a. Report an issue: GitHub.