fix(codex): log a late reply to a timed-out request instead of showing it in the chat (#23893)

* fix(codex): log a late reply to a timed-out request instead of showing it in the chat

* fix(codex): say a reply had no waiting request rather than an unknown id
This commit is contained in:
Brennan Benson
2026-09-29 13:19:48 -07:00
committed by GitHub
parent 18327d9665
commit ff59e2cd7b
3 changed files with 61 additions and 4 deletions
@@ -10,6 +10,7 @@ import {
import { classifyJsonRpcPrefix } from './codex-app-server-record-prefix'
const OVERSIZED_REQUEST_ERROR_CODE = -32001
const MAX_REMEMBERED_TIMEOUTS = 64
export type CodexPendingRequest = {
method: string
@@ -25,11 +26,26 @@ export function createCodexAppServerRecordDispatcher(input: {
}): {
addPending: (id: number, waiter: CodexPendingRequest) => void
deletePending: (id: number) => void
timeOutPending: (id: number) => void
failPending: (error: Error) => void
dispatch: (message: Record<string, unknown>) => void
rejectOversized: (rejected: NdjsonRejectedRecord & { kind: 'line-too-long' }) => void
} {
const pending = new Map<number, CodexPendingRequest>()
const timedOutMethods = new Map<number, string>()
const timeOutPending = (id: number): void => {
const waiter = pending.get(id)
if (!waiter) {
return
}
pending.delete(id)
timedOutMethods.set(id, waiter.method)
const oldest = timedOutMethods.keys().next()
if (timedOutMethods.size > MAX_REMEMBERED_TIMEOUTS && !oldest.done) {
timedOutMethods.delete(oldest.value)
}
}
const failPending = (error: Error): void => {
for (const waiter of pending.values()) {
@@ -72,7 +88,17 @@ export function createCodexAppServerRecordDispatcher(input: {
}
const waiter = pending.get(message.id)
if (!waiter) {
input.handlers.onUnhandledFrame?.('response:unmatched', message)
// Transport diagnostics, not conversation: only the request that gave up could have
// interpreted this reply, and it already reported its own outcome.
const timedOutMethod = timedOutMethods.get(message.id)
timedOutMethods.delete(message.id)
const error = isAppServerRecord(message.error) ? message.error.message : undefined
console.warn(
timedOutMethod
? `[codex-app-server] late reply to ${timedOutMethod} after timeout (id ${message.id})`
: `[codex-app-server] reply with no waiting request (id ${message.id})`,
...(typeof error === 'string' ? [error] : [])
)
return
}
pending.delete(message.id)
@@ -160,6 +186,7 @@ export function createCodexAppServerRecordDispatcher(input: {
return {
addPending: (id, waiter) => pending.set(id, waiter),
deletePending: (id) => pending.delete(id),
timeOutPending,
failPending,
dispatch,
rejectOversized