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

  1. Guard every request() call with a closed check, or track lifecycle so no requests are issued after close().
  2. Create a fresh client via CodexAppServerClient.connect() for a new session rather than reusing a closed one.
  3. 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

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


AI-assisted analysis of openai/codex-plugin-cc@db52e28f4d (2026-08-13). Data as JSON: /api/errors/b9011391186a3999. Report an issue: GitHub.