paperclipai/paperclip · error
Codex turn identity changed during start
Error message
Codex turn identity changed during start
What it means
startTurn validates that the turn id returned by the turn/start RPC matches the turn id announced by the optimistic turn/started notification. When the Codex app-server already reports a different active turn than the one the start response returned, failProtocol('turn_start_mismatch') is raised and this Error is thrown, marking the session protocol as broken. It protects against driver/provider disagreement about turn identity, which would corrupt terminal-event routing.
Source
Thrown at packages/paperclip-runner/src/drivers/codex/codex-harness-session.ts:268
// never observes the terminal turn ahead of turn.accepted.
releaseTurnStartSettled();
}
this.assertProtocolIntegrity();
const turn = record(response.turn);
const turnId = text(turn.id);
if (turnId.length === 0) {
// A start notification is only optimistic until the response validates.
// Clear it before released semantic/terminal waiters can observe an
// active turn for a request that was never accepted.
this.activeTurnId = null;
this.turnStarted = false;
throw new Error("Codex turn response omitted turn.id");
}
if (this.activeTurnId !== null && this.activeTurnId !== turnId) {
this.failProtocol(
"turn_start_mismatch",
"turn/start response disagreed with turn/started",
);
throw new Error("Codex turn identity changed during start");
}
this.activeTurnId ??= turnId;
if (dispositionOnlyRecovery) {
this.dispositionOnlyRecoveryTurnId = turnId;
}
this.emit("turn.accepted", { turnId }, { turnId });
if (this.interruptQueued) {
this.interruptQueued = false;
await this.#sendInterrupt(turnId, "queued_before_start");
}
return {
turnId,
effectiveCollaborationMode,
};
}
async steer(input: {View on GitHub (pinned to 01ad858492)
Solutions
- Ensure only one startTurn call is in flight per harness session; await the previous turn's completion or interrupt it before starting a new turn.
- Create a fresh harness session for the new turn instead of reusing one that may still have a pending active turn.
- Check the Codex app-server version for notification/response ordering bugs and upgrade the provider binary.
- Catch the error and restart the session; failProtocol has marked this session terminal so it cannot accept further turns.
Example fix
// before await Promise.all([session.startTurn(a), session.startTurn(b)]); // after await session.startTurn(a); await session.startTurn(b);
Defensive patterns
Strategy: try-catch
Validate before calling
if (session.hasActiveTurn?.()) throw new Error('session busy: cannot start a new turn'); Type guard
function isTurnIdentityMismatch(e: unknown): boolean { return e instanceof Error && e.message === 'Codex turn identity changed during start'; } Try / catch
try { await session.startTurn(input); } catch (e) { if (isTurnIdentityMismatch(e)) { await recreateSession(); } else throw e; } Prevention
- Never call startTurn concurrently on one harness session
- Await terminal events before starting the next turn
- Keep the Codex app-server binary up to date
- Recreate sessions after any protocol error — failProtocol marks them terminal
When it happens
Trigger: A turn/started notification arrives for a previous or concurrent turn while turn/start is pending, and the turn/start response then returns a different turn id than the one already stored in this.activeTurnId.
Common situations: Calling startTurn concurrently on the same harness session; a stale or buggy Codex app-server version emitting mismatched notifications; a session reused after a prior turn's terminal event was dropped.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Codex thread response omitted thread.id
- provider_initialize_protocol_error
- Codex turn response omitted turn.id
- Codex turn response omitted turn.id
- ${name} must be a JSON object
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/b3b29bdba42e1183.
Report an issue: GitHub.