openai/codex · error · ExecutorPluginMcpProviderError
failed to read MCP config for selected plugin `{plugin_id}`
Error message
failed to read MCP config for selected plugin `{plugin_id}` at `{path}`: {source} What it means
ExecutorPluginMcpProvider::load reads the plugin's MCP config — the manifest's paths.mcp_servers file, or the default <plugin_root>/.mcp.json. ReadConfig wraps the io error. Note the asymmetry: NotFound on the DEFAULT .mcp.json returns Ok(vec![]) (no servers), so ReadConfig means either the manifest-declared path failed, or a non-NotFound error (permissions, transport) hit the default path.
Source
Thrown at codex-rs/ext/mcp/src/executor_plugin/provider.rs:24
use codex_plugin::PluginResourceLocator;
use codex_plugin::ResolvedPlugin;
use codex_plugin::ResolvedPluginLocation;
use codex_plugin::manifest::PluginManifestMcpServers;
use codex_utils_path_uri::PathUri;
use codex_utils_path_uri::PathUriParseError;
use std::io;
use thiserror::Error;
const DEFAULT_MCP_CONFIG_FILE: &str = ".mcp.json";
/// Loads MCP declarations from resolved plugins through their owning executor.
#[derive(Clone, Copy, Debug, Default)]
pub(super) struct ExecutorPluginMcpProvider;
/// Failure to load an executor plugin's MCP declarations.
#[derive(Debug, Error)]
pub(super) enum ExecutorPluginMcpProviderError {
#[error("failed to read MCP config for selected plugin `{plugin_id}` at `{path}`: {source}")]
ReadConfig {
plugin_id: String,
path: PathUri,
#[source]
source: io::Error,
},
#[error(
"failed to resolve MCP config path `{relative_path}` below selected plugin `{plugin_id}` at `{root}`: {source}"
)]
InvalidConfigPath {
plugin_id: String,
root: PathUri,
relative_path: &'static str,
#[source]
source: PathUriParseError,
},
#[error("failed to parse MCP config for selected plugin `{plugin_id}` at `{path}`: {source}")]
ParseConfig {View on GitHub (pinned to 339751715c)
Solutions
- Confirm the file at the printed path exists and is readable in the plugin environment.
- If the plugin has no MCP servers, remove the paths.mcp_servers manifest entry — an absent default .mcp.json loads as empty.
- Fix the manifest path or repackage the plugin.
- Check executor environment connectivity when the io source is a transport error.
Defensive patterns
Strategy: try-catch
Try / catch
match mcp_provider.load(&plugin).await {
Ok(servers) => servers,
Err(ExecutorPluginMcpProviderError::ReadConfig { plugin_id, path, source }) => {
tracing::warn!(%plugin_id, %path, %source, "plugin MCP config unreadable; skipping");
Vec::new()
}
Err(e) => return Err(e.into()),
} Prevention
- Only declare paths.mcp_servers in the manifest when the file actually ships.
- Remember NotFound on the default .mcp.json is fine — it loads as zero servers.
- Fix file modes inside plugin environments during packaging.
When it happens
Trigger: Manifest declares paths.mcp_servers pointing at a missing or unreadable file; or the default .mcp.json exists but reading it fails with permission or executor transport errors.
Common situations: Plugin manifest references an MCP config that is not shipped; wrong file mode bits inside the plugin environment; remote executor environment unreachable.
Related errors
- failed to read app config for selected plugin `{plugin_id}`
- failed to resolve MCP config path `{relative_path}` below se
- failed to parse MCP config for selected plugin `{plugin_id}`
- Environment variable {env_var} for MCP server '{server_name}
- Invalid MCP server name '{server_name}': must match pattern
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/98a0e9e7459d6130.
Report an issue: GitHub.