Skip to content

Intermittent OpenAI streamed server_error (sequence_number:2) with gpt-5.3-codex; retries degrade session #16214

Closed as not planned
Disaster-Terminator/opencode
#3

Description

Description

Using OpenCode 1.2.15 with openai/gpt-5.3-codex, I intermittently get streamed JSON errors like:

{"type":"error","sequence_number":2,"error":{"type":"server_error","code":"server_error","message":"An error occurred while processing your request... [retrying in 2s attempt #2]"}}

When this happens, retries may continue but session quality degrades (manual intervention often needed).

Environment

  • OpenCode: 1.2.15
  • OS: macOS
  • Model: openai/gpt-5.3-codex
  • Config changes already applied:
    • provider.openai.options.timeout = 600000
    • compaction = { auto: true, prune: true, reserved: 15000 }

Steps to reproduce

  1. Start with debug logs enabled: opencode --log-level DEBUG --print-logs
  2. Use openai/gpt-5.3-codex
  3. Run medium/long coding prompts (tool use involved)
  4. Intermittently receive the streamed server_error envelope above

Expected

  • Robust handling/recovery for transient streamed provider errors
  • Clear surfaced diagnostics for retry classification and provider error details
  • Session should not degrade/stall after repeated transient server_error events

Actual

  • Intermittent streamed server_error
  • Retry attempts occur, but session can still degrade or require manual continue/retry

Related issues checked

I checked similar issues first:

This report is specifically about the OpenAI streamed error payload with sequence_number:2 + server_error on gpt-5.3-codex.

Notes

OpenAI status has had multiple Codex/Responses elevated-error incidents recently, so this might be a combination of upstream transient faults + client-side retry UX/resilience behavior.

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Mar 5, 2026
  2. github-actions commented on Mar 5, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    These issues (which you listed as "related issues checked") appear to overlap significantly with the behavior you're describing. Consider whether your report adds new information beyond what's already tracked there, or if a comment/upvote on an existing issue would be more appropriate.

  3. chindris-mihai-alexandru commented on Mar 5, 2026

    @chindris-mihai-alexandru
    Author

    I reviewed #12234, #12750, and #6983.

    Why I filed separately:

    If maintainers prefer consolidating under an existing issue, I’m fine closing this as duplicate and moving the payload/evidence there. I can also provide additional timestamped DEBUG logs if helpful.

  4. chindris-mihai-alexandru commented on Mar 5, 2026

    @chindris-mihai-alexandru
    Author

    Additional triage data:

    • This is intermittent and not reliably reproducible on demand.
    • I do have a captured real payload from session history:
      {"type":"error","sequence_number":2,"error":{"type":"server_error","code":"server_error","message":"... [retrying in 2s attempt #2]"}}
    • Captured in exported session ses_3414e160bffeM7NlG2Ae41uXUQ.

    Given intermittency, I’m providing concrete payload + session context rather than deterministic repro steps.
    If useful, I can attach a redacted DEBUG log bundle with nearby successful/failing runs and timestamps.

  5. chindris-mihai-alexandru commented on Mar 5, 2026

    @chindris-mihai-alexandru
    Author

    Update: I exported and redacted evidence from historical sessions/logs.

    Prepared artifacts:

    • opencode-error-snippets.txt (captured sequence_number:2 + server_error + retry envelope from exported session)
    • opencode-debug-snippets.txt (sanitized timestamp/model/session/error summary lines)
    • opencode-session-ses_3414e160bffeM7NlG2Ae41uXUQ.json (redacted session export)

    I can attach these if maintainers want the full bundle in this thread.

  6. chindris-mihai-alexandru commented on Mar 5, 2026

    @chindris-mihai-alexandru
    Author

    Superseding my prior note — attaching the redacted evidence inline now.

    opencode-error-snippets.txt

    session_id: ses_3414e160bffeM7NlG2Ae41uXUQ
    session_updated_unix_ms: 1772736548377
    captured_payload: {"type":"error","sequence_number":2,"error":{"type":"server_error","code":"serve... (click to expand) [retrying in 2s attempt #2]
    captured_payload: {"type":"error","sequence_number":2,"error":{"type":"server_error","code":"server_error","message":"An error occurred while processing your request... [retrying in 2s attempt #2]
    

    opencode-debug-snippets.txt

    2026-02-27T174331.log:155 | 2026-02-27T19:16:23 | ERROR | provider=openai | model=gpt-5.3-codex | session=ses_35f782c21ffettEeu5b3nwNqpz | agent=build | error=AI_APICallError
    2026-02-27T174331.log:160 | 2026-02-27T19:16:26 | ERROR | provider=openai | model=gpt-5.3-codex | session=ses_35f782c21ffettEeu5b3nwNqpz | agent=build | error=AI_APICallError
    2026-02-27T174331.log:165 | 2026-02-27T19:16:31 | ERROR | provider=openai | model=gpt-5.3-codex | session=ses_35f782c21ffettEeu5b3nwNqpz | agent=build | error=AI_APICallError
    2026-02-27T174331.log:170 | 2026-02-27T19:16:40 | ERROR | provider=openai | model=gpt-5.3-codex | session=ses_35f782c21ffettEeu5b3nwNqpz | agent=build | error=AI_APICallError
    2026-02-27T174331.log:179 | 2026-02-27T19:16:56 | ERROR | provider=openai | model=gpt-5.3-codex | session=ses_35f782c21ffettEeu5b3nwNqpz | agent=build | error=AI_APICallError
    2026-02-27T174331.log:184 | 2026-02-27T19:17:29 | ERROR | provider=openai | model=gpt-5.3-codex | session=ses_35f782c21ffettEeu5b3nwNqpz | agent=build | error=AI_APICallError
    

    I also have a fully redacted session export (opencode-session-ses_3414e160bffeM7NlG2Ae41uXUQ.json) ready if needed.

  7. chindris-mihai-alexandru commented on Mar 5, 2026

    @chindris-mihai-alexandru
    Author

    Attached the redacted session JSON as a secret gist:

    This is opencode-session-ses_3414e160bffeM7NlG2Ae41uXUQ.json referenced above.

  8. Techie5879 commented on Mar 10, 2026

    @Techie5879

    Seeing similar issues with gpt-5.4-high

  9. kiyoakii commented on Apr 22, 2026

    @kiyoakii

    getting the same errors now with gpt-5.4-high

  10. mirsella commented on Apr 22, 2026

    @mirsella
    Contributor

    has been happened a lot today for me. a thread is even completely dead and each message trigger it. other thread can take some messages before erroring again

  11. 0verhead commented on Apr 22, 2026

    @0verhead

    This morning this issue started happening to me constantly. It is basically unusable right now.

  12. mirsella commented on Apr 22, 2026

    @mirsella
    Contributor

    codex cli is working perfectly fine in the meantime

  13. Salloxy commented on Apr 27, 2026

    @Salloxy

    Same issue. Started happening today in OpenCode Windows

  14. rekram1-node commented on Apr 27, 2026

    @rekram1-node
    Contributor

    This should only happen on transient api errors from their server

  15. kiyoakii commented on Apr 27, 2026

    @kiyoakii

    at least opencode should retry? I've had moments where it's nearly not usable.

  16. mirsella commented on Apr 27, 2026

    @mirsella
    Contributor

    made a quick plugin with codex to auto retry (doesn't error less, just auto retry with some cooldown (1s, then 2sec, 3sec etc if continue to error).

    retry-server-errors.ts
    import type { Plugin } from "@opencode-ai/plugin";
    import type { AssistantMessage, Message, UserMessage } from "@opencode-ai/sdk";
    
    const RETRY_PROMPT = "continue";
    const QUICK_FAILURE_WINDOW_MS = 15_000;
    const MESSAGE_POLL_ATTEMPTS = 5;
    const MESSAGE_POLL_DELAY_MS = 200;
    const MESSAGE_FETCH_LIMIT = 24;
    
    type RetryState = {
    	backoffStep: number;
    	lastFailureAt: number;
    	pendingToken?: symbol;
    	pendingTimer?: ReturnType<typeof setTimeout>;
    	lastHandledMessageID?: string;
    };
    
    const globalState = globalThis as typeof globalThis & {
    	__opencodeRetryServerErrors?: Map<string, RetryState>;
    };
    
    const states =
    	globalState.__opencodeRetryServerErrors ??
    	(globalState.__opencodeRetryServerErrors = new Map<string, RetryState>());
    
    const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
    
    const getState = (sessionID: string): RetryState => {
    	const existing = states.get(sessionID);
    	if (existing) return existing;
    
    	const created: RetryState = {
    		backoffStep: 0,
    		lastFailureAt: 0,
    	};
    	states.set(sessionID, created);
    	return created;
    };
    
    const safeJsonParse = (value: string): unknown => {
    	try {
    		return JSON.parse(value);
    	} catch {
    		return undefined;
    	}
    };
    
    const isObject = (value: unknown): value is Record<string, unknown> =>
    	typeof value === "object" && value !== null;
    
    const unwrapProviderError = (message: string) => {
    	const parsed = safeJsonParse(message);
    	if (!isObject(parsed)) return undefined;
    	if (parsed.type !== "error") return undefined;
    
    	const inner = parsed.error;
    	return isObject(inner) ? inner : undefined;
    };
    
    const isRetryableError = (error: AssistantMessage["error"] | undefined): boolean => {
    	if (!error) return false;
    
    	const rawMessage =
    		error && "data" in error && isObject(error.data) && typeof error.data.message === "string"
    			? error.data.message
    			: "";
    	const nested = rawMessage ? unwrapProviderError(rawMessage) : undefined;
    	const nestedCode = typeof nested?.code === "string" ? nested.code : "";
    	const nestedType = typeof nested?.type === "string" ? nested.type : "";
    	const nestedMessage = typeof nested?.message === "string" ? nested.message : "";
    
    	return (
    		nestedCode === "server_error" ||
    		nestedCode === "server_is_overloaded" ||
    		nestedType === "server_error" ||
    		nestedType === "service_unavailable_error" ||
    		rawMessage.includes("An error occurred while processing your request") ||
    		rawMessage.includes("Our servers are currently overloaded") ||
    		nestedMessage.includes("An error occurred while processing your request") ||
    		nestedMessage.includes("Our servers are currently overloaded")
    	);
    };
    
    const sortNewestFirst = <T extends Message>(messages: T[]): T[] =>
    	[...messages].sort((a, b) => b.time.created - a.time.created);
    
    const fetchMessages = async (client: Parameters<Plugin>[0]["client"], sessionID: string) => {
    	const response = await client.session.messages({
    		path: { id: sessionID },
    		query: { limit: MESSAGE_FETCH_LIMIT },
    	});
    	return response.data ?? [];
    };
    
    const pollRetryableTail = async (
    	client: Parameters<Plugin>[0]["client"],
    	sessionID: string,
    ): Promise<AssistantMessage | undefined> => {
    	for (let attempt = 0; attempt < MESSAGE_POLL_ATTEMPTS; attempt++) {
    		const entries = await fetchMessages(client, sessionID);
    		const infos = entries.map((entry) => entry.info);
    		const tail = sortNewestFirst(infos)[0];
    
    		if (
    			tail &&
    			tail.role === "assistant" &&
    			isRetryableError(tail.error)
    		) {
    			return tail;
    		}
    
    		if (attempt + 1 < MESSAGE_POLL_ATTEMPTS) {
    			await wait(MESSAGE_POLL_DELAY_MS);
    		}
    	}
    
    	return undefined;
    };
    
    const hasNewerMessages = async (
    	client: Parameters<Plugin>[0]["client"],
    	sessionID: string,
    	since: number,
    ): Promise<boolean> => {
    	const entries = await fetchMessages(client, sessionID);
    	return entries.some((entry) => entry.info.time.created > since);
    };
    
    const fetchMessageEntry = async (
    	client: Parameters<Plugin>[0]["client"],
    	sessionID: string,
    	messageID: string,
    ): Promise<{ info: Message; parts: Array<{ type: string; text?: string; synthetic?: boolean; ignored?: boolean }> } | undefined> => {
    	const response = await client.session.message({
    		path: {
    			id: sessionID,
    			messageID,
    		},
    	});
    	return response.data as
    		| { info: Message; parts: Array<{ type: string; text?: string; synthetic?: boolean; ignored?: boolean }> }
    		| undefined;
    };
    
    const deleteMessage = async (client: Parameters<Plugin>[0]["client"], sessionID: string, messageID: string): Promise<void> => {
    	const transport = (client.session as any)?._client ?? (client as any)?._client;
    	if (!transport?.delete) {
    		throw new Error("OpenCode SDK transport does not expose delete()");
    	}
    
    	await transport.delete({
    		url: "/session/{id}/message/{messageID}",
    		path: {
    			id: sessionID,
    			messageID,
    		},
    	});
    };
    
    const isPlainContinueMessage = (
    	entry: { info: Message; parts: Array<{ type: string; text?: string; synthetic?: boolean; ignored?: boolean }> } | undefined,
    ): entry is { info: UserMessage; parts: Array<{ type: string; text?: string; synthetic?: boolean; ignored?: boolean }> } => {
    	if (!entry || entry.info.role !== "user") return false;
    	if (entry.parts.length !== 1) return false;
    
    	const [part] = entry.parts;
    	return (
    		part.type === "text" &&
    		!part.synthetic &&
    		!part.ignored &&
    		(part.text ?? "").trim() === RETRY_PROMPT
    	);
    };
    
    const getAssistantChildren = (
    	entries: Array<{ info: Message; parts: Array<{ type: string; text?: string; synthetic?: boolean; ignored?: boolean }> }>,
    	parentID: string,
    ): AssistantMessage[] =>
    	entries
    		.map((entry) => entry.info)
    		.filter(
    			(message): message is AssistantMessage =>
    				message.role === "assistant" && message.parentID === parentID,
    		);
    
    const removeRetryChainForParent = async (
    	client: Parameters<Plugin>[0]["client"],
    	sessionID: string,
    	parentID: string,
    	entries: Array<{ info: Message; parts: Array<{ type: string; text?: string; synthetic?: boolean; ignored?: boolean }> }>,
    	skipMessageID: string | undefined,
    	log: (level: "debug" | "info" | "warn" | "error", message: string, extra?: Record<string, unknown>) => Promise<void>,
    ) => {
    	const children = getAssistantChildren(entries, parentID).filter((child) => child.id !== skipMessageID);
    	const hasSuccessfulChild = children.some((child) => !child.error);
    	if (hasSuccessfulChild) {
    		return false;
    	}
    
    	for (const child of children) {
    		await deleteMessage(client, sessionID, child.id).catch(async (deleteError: unknown) => {
    			const message = deleteError instanceof Error ? deleteError.message : String(deleteError);
    			await log("warn", "failed to delete sibling retry message", {
    				sessionID,
    				messageID: child.id,
    				parentID,
    				error: message,
    			});
    		});
    	}
    
    	await deleteMessage(client, sessionID, parentID).catch(async (deleteError: unknown) => {
    		const message = deleteError instanceof Error ? deleteError.message : String(deleteError);
    		await log("warn", "failed to delete retry parent continue message", {
    			sessionID,
    			messageID: parentID,
    			error: message,
    		});
    	});
    
    	return true;
    };
    
    const scheduleDelayMs = (state: RetryState, now: number): number => {
    	if (state.lastFailureAt && now - state.lastFailureAt < QUICK_FAILURE_WINDOW_MS) {
    		state.backoffStep += 1;
    	} else {
    		state.backoffStep = 0;
    	}
    
    	state.lastFailureAt = now;
    	return state.backoffStep * 1000;
    };
    
    export const RetryServerErrorsPlugin: Plugin = async ({ client }) => {
    	const log = async (level: "debug" | "info" | "warn" | "error", message: string, extra?: Record<string, unknown>) => {
    		await client.app
    			.log({
    				body: {
    					service: "retry-server-errors",
    					level,
    					message,
    					extra,
    				},
    			})
    			.catch(() => undefined);
    	};
    
    	const toast = async (message: string, variant: "info" | "warning" | "error" = "warning") => {
    		await client.tui
    			.showToast({
    				body: {
    					message,
    					variant,
    					duration: 2500,
    				},
    			})
    			.catch(() => undefined);
    	};
    
    	return {
    		event: async ({ event }) => {
    			if (event.type !== "session.error") return;
    
    			const { sessionID, error } = event.properties;
    			if (!sessionID || !isRetryableError(error)) return;
    
    			const failingMessage = await pollRetryableTail(client, sessionID);
    			if (!failingMessage) return;
    
    			const state = getState(sessionID);
    			if (state.lastHandledMessageID === failingMessage.id) return;
    			state.lastHandledMessageID = failingMessage.id;
    
    			if (state.pendingTimer) {
    				clearTimeout(state.pendingTimer);
    				state.pendingTimer = undefined;
    			}
    
    			const delayMs = scheduleDelayMs(state, Date.now());
    			const token = Symbol(failingMessage.id);
    			state.pendingToken = token;
    
    			const recentEntries = await fetchMessages(client, sessionID);
    
    			await deleteMessage(client, sessionID, failingMessage.id).catch(async (deleteError: unknown) => {
    				const message = deleteError instanceof Error ? deleteError.message : String(deleteError);
    				await log("warn", "failed to delete retryable error message", {
    					sessionID,
    					messageID: failingMessage.id,
    					error: message,
    				});
    			});
    
    			const parentEntry = await fetchMessageEntry(client, sessionID, failingMessage.parentID).catch(
    				() => undefined,
    			);
    			if (isPlainContinueMessage(parentEntry)) {
    				await removeRetryChainForParent(
    					client,
    					sessionID,
    					parentEntry.info.id,
    					recentEntries,
    					failingMessage.id,
    					log,
    				);
    			}
    
    			if (delayMs > 0) {
    				await toast(`Retrying in ${delayMs / 1000}s`, "warning");
    			}
    
    			state.pendingTimer = setTimeout(async () => {
    				if (state.pendingToken !== token) return;
    
    				try {
    					if (await hasNewerMessages(client, sessionID, failingMessage.time.created)) {
    						await log("info", "skipped retry because the session moved forward", {
    							sessionID,
    							messageID: failingMessage.id,
    						});
    						return;
    					}
    
    					const parentEntry = await fetchMessageEntry(
    						client,
    						sessionID,
    						failingMessage.parentID,
    					).catch(() => undefined);
    					const parentUserMessage =
    						parentEntry?.info.role === "user" ? parentEntry.info : undefined;
    
    					await client.session.promptAsync({
    						path: { id: sessionID },
    						body: {
    							agent: parentUserMessage?.agent,
    							model: parentUserMessage?.model,
    							parts: [{ type: "text", text: RETRY_PROMPT }],
    						},
    					});
    
    					await log("info", "scheduled retry for transient provider error", {
    						sessionID,
    						messageID: failingMessage.id,
    						delayMs,
    						agent: parentUserMessage?.agent,
    						modelID: failingMessage.modelID,
    						providerID: failingMessage.providerID,
    					});
    				} catch (retryError) {
    					const message = retryError instanceof Error ? retryError.message : String(retryError);
    					await log("error", "failed to schedule retry", {
    						sessionID,
    						messageID: failingMessage.id,
    						error: message,
    					});
    					await toast(`Retry failed: ${message}`, "error");
    				} finally {
    					if (state.pendingToken === token) {
    						state.pendingToken = undefined;
    					}
    					if (state.pendingTimer) {
    						clearTimeout(state.pendingTimer);
    						state.pendingTimer = undefined;
    					}
    				}
    			}, delayMs);
    		},
    	};
    };
  17. hubertlepicki commented on Apr 27, 2026

    @hubertlepicki

    So I am getting these errors as well a bit randomly on 5.4 but consistently and always on GPT-5.5. Every prompt I send just fails, while Codex works no problem.

    This is also only happening on my employer's Enterprise (EU) OpenAI account, and on my other account I see no such problem at all.

    What I found so far is that, for whatever reason, OpenAI is validaging "originator" and "User-Agent" headers on one of the accounts, and refusing to serve OpenCode, while serving Codex requests properly.

    gpt-5.5 fails in OpenCode on our enterprise account, while the same account works in Codex.
    We traced this to the HTTP requests sent to the ChatGPT Codex backend. OpenCode and Codex hit the same backend with the same account and equivalent minimal payload, but they identify themselves differently via these headers:
    - originator
    - User-Agent
    Observed behavior:
    - OpenCode-style headers:
      - originator: opencode
      - User-Agent: opencode/...
      - result: backend server_error
    - Codex-style headers:
      - originator: codex_cli_rs
      - User-Agent: codex_cli_rs/...
      - result: request succeeds
    We also isolated the variables:
    - changing only originator was not enough
    - changing only User-Agent was not enough
    - changing both to Codex-style made the same gpt-5.5 request succeed
    Conclusion:
    OpenCode on our enterprise account does not work with gpt-5.5, but Codex does. The failure is tied to how the client identifies itself to the backend, specifically through originator and User-Agent. The most likely explanation is that OpenAI is intentionally allowing gpt-5.5 for first-party Codex clients while blocking or not supporting it for non-Codex agents such as OpenCode on this account.
    

    I have tested the requests manually, they consistently fail with gpt-5.5 and "opencode" originator/User-Agent, but succeed if I spoof it to be "codex_cli_rs" (for User-Agent it's a longer string).

    These headers (User-Agent and originator) do not appear to be configurable within OpenCode, bue even if they were, I do not know if we're safe overriding it.

    I do not see a configuration option in the account I have access to to disable/enable it, but maybe someone knows how to disable that header checking so I can keep using OpenCode at work that would be great...

  18. Salloxy commented on Apr 30, 2026

    @Salloxy

    Was anyone able to solve this?

  19. removed
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  20. spark4862 commented on Jun 2, 2026

    @spark4862
    Contributor

    Opened #30323 rebased on current dev with broader OpenAI/Codex stream error coverage (stream_read_error, overload, rate limit), including parseStreamError, retryable(), and fromError(Error.message) paths plus regression tests. Would appreciate a 👍/review if this matches what you are seeing — especially for Codex gpt-5.x unattended runs.

  21. lyjjl commented on Jun 26, 2026

    @lyjjl

    Environment: OpenCode CLI 1.17.11 on Linux, using the OpenAI provider with gpt-5.5.
    环境:Linux 、 OpenCode CLI 1.17.11、 OpenAI provider、gpt-5.5。

    I noticed that this issue may be related to having too many MCP servers enabled.
    我注意到这个问题可能和启用了过多的 MCP server 有关。

    Some MCP servers register a large number of tools, which may exceed certain OpenAI-side limits or trigger unstable behavior (not sure).
    某些 MCP server 会注册大量工具,这可能会超过 OpenAI 侧的某些限制,或触发不稳定行为 (不确定)。

    In my case, the error seemed to happen when the model was about to use tools.
    就我而言,错误似乎是在模型准备调用工具时发生的。

    I am not sure yet whether the same issue also exists in Codex CLI.
    我目前还不确定 Codex CLI 是否也存在同类问题。

    After I used the /mcps command to disable some unnecessary MCP servers, especially the ones registering many tools, it started working normally again.
    我使用 /mcps 命令禁用了一些不必要的 MCP server(尤其是会注册大量工具的 MCP server) 之后,它就恢复正常了。

    If you are seeing similar errors, it may be worth trying to disable unnecessary MCP servers and reduce the total number of registered tools.
    如果你遇到了类似的错误,可以尝试禁用不必要的 MCP server,减少已注册工具的总数量。

  22. github-actions commented on Aug 26, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions