{"record":{"id":"42611e8450bed454","repo":"Hmbown/CodeWhale","slug":"active-plugin-registry-is-missing-its-pre-dotenv-e","errorCode":null,"errorMessage":"active plugin registry is missing its pre-dotenv environment snapshot","messagePattern":"active plugin registry is missing its pre-dotenv environment snapshot","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":3657,"sourceCode":"fn merge_plugin_mcp_servers(\n    config: McpConfig,\n    registry: &crate::plugins::PluginRegistry,\n) -> Result<McpConfig> {\n    let Some(state_path) = registry.state_path().map(Path::to_path_buf) else {\n        return Ok(config);\n    };\n    let plugins = registry\n        .active_plugins()\n        .into_iter()\n        .filter_map(|plugin| {\n            plugin\n                .authority(state_path.clone(), registry.workspace().to_path_buf())\n                .map(|authority| (plugin.name().to_string(), plugin.clone(), authority))\n        })\n        .collect::<Vec<_>>();\n\n    let host_environment = registry.host_environment().ok_or_else(|| {\n        anyhow::anyhow!(\"active plugin registry is missing its pre-dotenv environment snapshot\")\n    })?;\n    merge_plugin_mcp_servers_from_plugins_with_environment(config, plugins, host_environment)\n}\n\nfn merge_plugin_mcp_servers_from_plugins_with_environment(\n    mut config: McpConfig,\n    plugins: impl IntoIterator<\n        Item = (\n            String,\n            crate::plugins::types::LoadedPlugin,\n            crate::plugins::types::PluginAuthority,\n        ),\n    >,\n    host_environment: Arc<crate::plugins::HostEnvironment>,\n) -> Result<McpConfig> {\n    for (plugin_name, plugin, authority) in plugins {\n        // Adapter-level denial keeps headless paths fail-closed even if a\n        // future caller accidentally passes the full inventory instead of the","sourceCodeStart":3639,"sourceCodeEnd":3675,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp.rs#L3639-L3675","documentation":"When merging plugin-contributed MCP servers, the manager needs the process environment as it was before any dotenv/.env overlays were applied, so plugin servers get the host environment rather than project-scoped values. That snapshot (registry.host_environment()) is captured when the plugin registry is built; if it is absent the merge fails on this invariant instead of silently using the wrong environment.","triggerScenarios":"Calling merge_plugin_mcp_servers (directly or via manager bootstrap) with a registry constructed without capturing the pre-dotenv environment - e.g. a custom or test-built registry using a lower-level constructor, or version skew after an upgrade.","commonSituations":"Upgrading to a version where registry construction changed; embedding the registry in tests or external tools that bypass the standard bootstrap path.","solutions":["Build the registry through the normal bootstrap API that snapshots the pre-dotenv environment","Update codewhale so all crates are the same version and the registry always records host_environment","If this reproduces with the standard bootstrap, file a bug - the invariant should always hold"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Verify the invariant before merging plugin MCP servers:\nif registry.host_environment().is_none() {\n    anyhow::bail!(\"registry built without pre-dotenv snapshot; rebuild it via the standard bootstrap\");\n}\nmerge_plugin_mcp_servers(&mut config, &registry)?;","typeGuard":null,"tryCatchPattern":"match merge_plugin_mcp_servers(&mut config, &registry) {\n    Err(e) if e.to_string().contains(\"pre-dotenv environment snapshot\") => {\n        // rebuild the registry through the supported constructor and retry once\n    }\n    other => other,\n}","preventionTips":["Always construct the plugin registry through the documented bootstrap path that captures host_environment","Keep all codewhale crates at one version so registry construction and consumers agree","In tests, use the real registry factory rather than hand-assembled structs"],"tags":["mcp","plugins","environment","invariant"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}