openai/codex-plugin-cc · error · Error
codex app-server client is closed.
Error message
codex app-server client is closed.
What it means
AppServerClientBase.request() is the JSON-RPC send method. It checks this.closed first; if close() was already called (or handleExit set closed state), any further request() throws synchronously. This prevents sending on a torn-down transport. Note notify() silently returns when closed, but request() throws because a response is expected.
Source
Thrown at plugins/codex/scripts/lib/app-server.mjs:88
this.exitPromise = new Promise((resolve) => {
this.resolveExit = resolve;
});
}
setNotificationHandler(handler) {
this.notificationHandler = handler;
}
/**
* @template {AppServerMethod} M
* @param {M} method
* @param {import("./app-server-protocol").AppServerRequestParams<M>} params
* @returns {Promise<import("./app-server-protocol").AppServerResponse<M>>}
*/
request(method, params) {
if (this.closed) {
throw new Error("codex app-server client is closed.");
}
const id = this.nextId;
this.nextId += 1;
return new Promise((resolve, reject) => {
this.pending.set(id, { resolve, reject, method });
this.sendMessage({ id, method, params });
});
}
notify(method, params = {}) {
if (this.closed) {
return;
}
this.sendMessage({ method, params });
}
View on GitHub (pinned to db52e28f4d)
Solutions
- Guard every request() call with a closed check, or track lifecycle so no requests are issued after close().
- Create a fresh client via CodexAppServerClient.connect() for a new session rather than reusing a closed one.
- Ensure close() is the last operation and awaited before the client goes out of scope.
Example fix
// before
await client.close();
await client.request('turn/start', params); // throws
// after
const result = await client.request('turn/start', params);
await client.close(); Defensive patterns
Strategy: try-catch
Validate before calling
if (client.closed) throw new Error('client closed; reconnect'); Type guard
/** @param {{closed: boolean}} c @returns {boolean} */
function isClientOpen(c) { return !c.closed; } Try / catch
try {
await client.request(method, params);
} catch (e) {
if (/is closed/.test(e.message)) { /* recreate client */ }
else throw e;
} Prevention
- Never call request() after close(); recreate the client instead.
- Centralize request issuance behind a lifecycle-aware wrapper.
When it happens
Trigger: Calling client.request(...) after await client.close(), or after the underlying process/socket exited (handleExit does not set this.closed, but a subsequent manual close does). Also calling request during shutdown sequencing.
Common situations: A shutdown race where the application closes the client but a pending turn/review call still issues request(); retry logic that does not check closed state.
Related errors
- codex app-server stdin is not available.
- codex app-server broker connection is not connected.
- Task ${activeTask.id} is still running. Use /codex:status be
- No previous Codex task thread was found for this repository.
- No stored job found for ${options["job-id"]}.
AI-assisted analysis of openai/codex-plugin-cc@db52e28f4d (2026-08-13).
Data as JSON: /api/errors/b9011391186a3999.
Report an issue: GitHub.