openai/codex · error
codex_apps MCP server is unavailable
Error message
codex_apps MCP server is unavailable
What it means
read_resource_for_call resolves which app tool produced a widget resource (by call_id in resource_origins), then reads it through the current binding of the reserved codex_apps MCP server. current_binding_for_call returned None: the hosted apps server has no live connection right now (Apps not enabled in this session, the server dropped by a config replace, or its connection failed), so the resource read cannot proceed.
Source
Thrown at codex-rs/codex-mcp/src/runtime.rs:234
.restore_checkpoint(checkpoint);
}
/// Reads a widget through the current binding of the app tool that produced it.
pub async fn read_resource_for_call(
&self,
thread_id: ThreadId,
call_id: &str,
uri: &str,
) -> anyhow::Result<ReadResourceResult> {
let origin = self
.resource_origins
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.find(call_id)?;
let binding = self
.current_binding_for_call(crate::CODEX_APPS_MCP_SERVER_NAME)
.await
.ok_or_else(|| anyhow::anyhow!("codex_apps MCP server is unavailable"))?;
origin.read(&binding, thread_id, uri).await
}
pub async fn new(input: McpRuntimeInput) -> Self {
let runtime = Self::empty(input.config.prefix_mcp_tool_names);
runtime.replace(input).await;
runtime
}
/// Reconciles configured servers and publishes their immutable runtime snapshot.
pub async fn replace(&self, input: McpRuntimeInput) {
let current = self.current.load_full();
let mut reconnect = McpReconnectGuard {
pending: &self.reconnect_pending,
claimed: self.reconnect_pending.swap(false, Ordering::AcqRel),
};
self.publish(View on GitHub (pinned to 339751715c)
Solutions
- Ensure the Apps/codex_apps feature and its hosted server are enabled, then restart the session so a live binding exists
- Re-run the originating tool call: a fresh call registers a new origin and binding for the read
- If the read is a best-effort UI refresh, degrade gracefully (render stale/placeholder content) instead of propagating the error
Example fix
// before
let result = runtime.read_resource_for_call(thread_id, call_id, uri).await?;
// after: degrade when the apps server is unavailable
let result = match runtime.read_resource_for_call(thread_id.clone(), call_id, uri).await {
Ok(r) => r,
Err(err) if err.to_string().contains("codex_apps MCP server is unavailable") => return render_stale(call_id),
Err(err) => return Err(err),
}; Defensive patterns
Strategy: try-catch
Try / catch
match runtime.read_resource_for_call(thread_id, call_id, uri).await {
Ok(result) => { /* render */ }
Err(err) if err.to_string().contains("codex_apps MCP server is unavailable") => { /* stale or placeholder render; optionally retry after the next runtime replace */ }
Err(err) => return Err(err),
} Prevention
- Treat widget resource reads as refreshable: cache last-good content keyed by call_id
- Check that Apps are enabled before issuing reads for app-originated resources
- Re-establish the MCP runtime (session restart) after config changes that touch apps
When it happens
Trigger: Reading a widget resource for an older tool call (e.g. re-rendering an app widget from thread history) after the MCP runtime replaced connections without a codex_apps server, when the Apps feature is disabled, or while the codex_apps connection is down or restarting.
Common situations: Resuming a thread whose widgets came from apps now disabled or reconfigured; enterprise config removing apps mid-session; transient connection drop during a resource read.
Related errors
- MCP server '{server}' is not registered by the hosted runtim
- command is required
- exactly one of --command or --url must be provided
- No MCP server named '{name}' found.
- Environment variable {env_var} for MCP server '{server_name}
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/8968bb7258062532.
Report an issue: GitHub.