What Happens When Codex Returns an Error During Review or Task Execution
When Codex returns an error during a review or task execution, the openai/codex-plugin-cc captures the error in a TurnCaptureState object, emits a failed progress notification, and exposes the error details in the result object for caller handling, while automatically retrying broker transport failures via direct connection fallback.
The openai/codex-plugin-cc repository provides a plugin interface for executing Codex reviews and tasks within continuous integration workflows. Understanding the error handling flow is essential for building resilient automation that can respond to network failures, authentication problems, or runtime exceptions gracefully.
Error Capture and Notification Flow
When the Codex app-server encounters an error, it communicates the failure through a structured JSON-RPC notification that the plugin processes and stores for later inspection.
JSON-RPC Error Notification Handling
The Codex app-server sends a notification with method: "error" containing the error payload. In plugins/codex/scripts/lib/codex.mjs, the notification handler routes this to applyTurnNotification, which stores the error and updates the progress state:
// plugins/codex/scripts/lib/codex.mjs
case "error":
state.error = message.params.error;
emitProgress(state.onProgress, `Codex error: ${message.params.error.message}`, "failed");
break;
This code populates state.error with the full error object and signals a failed phase to any registered progress listeners, providing immediate visibility into the failure.
Turn State Population
The captureTurn function awaits the turn’s completion promise. When an error has been recorded in the state, the promise resolves with the populated TurnCaptureState, ensuring the error persists alongside other turn metadata like threadId and turnId.
Error Propagation to Callers
High-level helper functions expose errors through standardized result objects, allowing calling code to implement conditional logic based on the failure type.
Result Object Structure
Both runAppServerReview and runAppServerTurn return a result object that explicitly includes the captured error:
// plugins/codex/scripts/lib/codex.mjs
return {
status: buildResultStatus(turnState),
threadId: turnState.threadId,
turnId: turnState.turnId,
...,
error: turnState.error,
stderr: cleanCodexStderr(client.stderr)
};
Callers can inspect result.error to determine whether to retry the operation, surface a user-friendly message, or abort the workflow entirely.
Error Inspection Patterns
The error object contains standard properties including message and code, enabling programmatic error classification. For example, network-related errors like ENETUNREACH or authentication failures can be distinguished and handled with specific recovery strategies.
Automatic Retry Mechanism for Transport Failures
The plugin implements transparent resilience for transport-layer failures by detecting broker connection issues and automatically falling back to direct connections.
Broker to Direct Connection Fallback
If the error originates from the shared broker transport—specifically when encountering BROKER_BUSY_RPC_CODE, ENOENT, or ECONNREFUSED—the withAppServer wrapper triggers an automatic retry:
// plugins/codex/scripts/lib/codex.mjs
const brokerRequested = client?.transport === "broker" || Boolean(process.env[BROKER_ENDPOINT_ENV]);
const shouldRetryDirect =
(client?.transport === "broker" && error?.rpcCode === BROKER_BUSY_RPC_CODE) ||
(brokerRequested && (error?.code === "ENOENT" || error?.code === "ECONNREFUSED"));
When shouldRetryDirect evaluates to true, the wrapper creates a new direct client and re-executes the original function, providing seamless recovery without manual intervention.
Code Examples
Handling Errors in Review Operations
Use runAppServerReview to execute a code review and handle potential failures:
import { runAppServerReview } from "./plugins/codex/scripts/lib/codex.mjs";
async function doReview(cwd) {
try {
const result = await runAppServerReview(cwd, {
onProgress: console.log,
target: { type: "branch", ref: "main" }
});
if (result.error) {
console.error("Review failed:", result.error.message);
// Decide whether to retry, show UI, or abort.
return;
}
console.log("Review text:", result.reviewText);
} catch (e) {
// Unexpected exception (e.g., missing Codex binary)
console.error("Unable to start review:", e.message);
}
}
Detecting Failures in Task Execution
When running a task with runAppServerTurn, check the result error property to detect execution failures:
import { runAppServerTurn } from "./plugins/codex/scripts/lib/codex.mjs";
async function runTask(cwd, prompt) {
const result = await runAppServerTurn(cwd, {
prompt,
onProgress: console.log
});
if (result.error) {
// The Codex app-server reported an error during execution.
console.error("Task error:", result.error.message);
// Example: retry if the error is a transient network issue.
if (result.error.code === "ENETUNREACH") {
// retry logic …
}
return;
}
console.log("Final answer:", result.finalMessage);
}
Implementing Safe Operations with Automatic Retry
Leverage withAppServer to benefit from automatic broker-to-direct fallback:
import { withAppServer } from "./plugins/codex/scripts/lib/codex.mjs";
async function safeOperation(cwd, operation) {
return await withAppServer(cwd, async (client) => {
// `operation` may throw an error that triggers automatic retry.
return await operation(client);
});
}
Summary
- Error Capture: The plugin stores Codex errors in
TurnCaptureState.errorwhen receiving JSON-RPC error notifications from the app-server. - Progress Reporting: Failed operations emit progress updates with a "failed" phase through
emitProgress, enabling real-time UI updates. - Result Exposure: High-level functions return result objects containing
error,stderr, and turn identifiers, allowing callers to inspect failure details. - Transport Resilience: The
withAppServerwrapper automatically retries operations using a direct connection when broker transport fails with specific error codes (ENOENT,ECONNREFUSED, orBROKER_BUSY_RPC_CODE).
Frequently Asked Questions
How does the plugin distinguish between different types of Codex errors?
The plugin relies on standard error properties such as error.code and error.rpcCode to classify failures. Transport errors like ECONNREFUSED or ENOENT trigger automatic retry logic, while application-level errors (such as authentication failures) are captured in TurnCaptureState and returned to the caller for manual handling.
Can I disable the automatic broker retry behavior?
The automatic retry logic is embedded in the withAppServer function within plugins/codex/scripts/lib/codex.mjs. To disable it, you would need to avoid using withAppServer and instead instantiate the CodexAppServerClient directly from plugins/codex/scripts/lib/app-server.mjs, managing connection failures manually in your calling code.
What information is included in the error object returned by runAppServerReview?
The error object contains at minimum a message property describing the failure, and often includes a code property for programmatic identification. For broker-related failures, an rpcCode property may also be present. The full error object is passed directly from the Codex app-server's JSON-RPC notification to the result object.
Where should I check for errors when implementing a custom Codex workflow?
Always inspect the error property of the result object returned by runAppServerReview, runAppServerTurn, or captureTurn. Additionally, monitor the stderr property for auxiliary error output from the Codex client process, which may contain diagnostic information not present in the structured error object.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →