What Happens When a Codex Job Is Cancelled Mid-Execution

When a Codex job is cancelled mid-execution, the system terminates the underlying process, attempts to interrupt any remote model turn, updates the persistent state file to "cancelled", and returns a JSON confirmation to the user.

The openai/codex-plugin-cc repository implements a robust cancellation pipeline that safely halts running jobs whether they are executed locally or brokered through a shared application server. When a user issues the /codex:cancel command or invokes the cancellation script directly, the request traverses a chain of modules—from the companion CLI entry point to the core job-control logic—to ensure clean termination and accurate state persistence.

The Cancellation Pipeline

The cancellation process follows six distinct stages, each handled by specific modules in the codebase to ensure safe interruption and state consistency.

Command Dispatch via the Codex Companion

The cancellation request originates at the Codex companion entry point located in plugins/codex/scripts/codex-companion.mjs. This script parses command-line arguments and routes the cancel request to the job-control logic. According to the source code at line 86, the companion validates the input flags (such as --json for machine-readable output) before forwarding the operation to the underlying control module.

Job Lookup and Validation

The job-control module (plugins/codex/scripts/lib/job-control.mjs) receives the request and performs a lookup to identify the target job. If no specific job ID is provided, the system searches for a single active job in the session state. As implemented in lines 300–307, the logic throws descriptive errors when multiple active jobs exist (requiring disambiguation) or when no active jobs are found. This validation prevents accidental termination of the wrong process.

Turn Interrupt for Brokered Jobs

For jobs brokered through the shared app-server, the cancellation routine attempts a turn-interrupt operation before killing the underlying process. This signals the remote executor to stop the current model turn gracefully. The system records the outcome of this attempt in the cancellation payload using the boolean fields turnInterruptAttempted and turnInterrupted, as shown in the test suite at tests/runtime.test.mjs lines 1771–1779. This mechanism ensures that remote resources are released properly rather than being abruptly severed.

Process Termination and State Update

Once the target job is identified, the system executes the termination sequence:

  • The process ID (pid) is killed using standard process termination signals
  • The pid field is cleared from the job record
  • The job status is set to "cancelled"
  • The cancelledAt timestamp is recorded

This logic is implemented in plugins/codex/scripts/lib/job-control.mjs at lines 113–114. The immediate state update ensures that the job cannot be resumed or mistaken for an active process.

Persistence and User Feedback

The updated job object—including the nullified pid, "cancelled" status, and cancelledAt timestamp—is written to the on-disk state file. As confirmed in tests/runtime.test.mjs lines 1628–1633, this persistence guarantees that subsequent /codex:status queries reflect the cancellation accurately.

The CLI then outputs a JSON payload (or human-readable message) confirming the operation. The response includes the jobId, final status, and turn-interrupt results, as validated in tests/runtime.test.mjs lines 1776–1780. If the job was already completed, failed, or previously cancelled, the system returns a helpful error message (see job-control.mjs line 172) instead of attempting termination.

How to Cancel a Codex Job

You can cancel jobs using the slash command in compatible editors or via the companion CLI directly.

Cancel the currently active job (only works if exactly one job is running):

/codex:cancel

Cancel a specific job by ID with JSON output:

/codex:cancel my-task-123 --json

Via the companion script:

node scripts/codex-companion.mjs cancel my-task-123 --json

Programmatic Cancellation in Node.js

You can invoke the same cancellation logic programmatically using the job-control module:

import { cancelJob } from "./plugins/codex/scripts/lib/job-control.mjs";

// Cancel by job ID with JSON output enabled
const result = await cancelJob({ jobId: "my-task-123", json: true });

console.log(result);
// {
//   jobId: "my-task-123",
//   status: "cancelled",
//   turnInterruptAttempted: true,
//   turnInterrupted: true
// }

Cancellation States and Error Handling

The system enforces strict state validation to prevent invalid operations:

  • Already completed jobs: Returns an error indicating the job has finished (line 172 in job-control.mjs)
  • Already cancelled jobs: Returns a descriptive error preventing duplicate cancellation attempts
  • Failed jobs: Returns an error stating the job has already failed and cannot be cancelled

The following table maps job states to cancellation behavior:

Current State Cancellation Result
running Process killed, state set to cancelled
cancelled Error: "Job already cancelled"
completed Error: "Job already completed"
failed Error: "Job already failed"

Summary

  • Command routing: Cancellation requests enter through codex-companion.mjs and flow to job-control.mjs
  • Turn interrupt: Brokered jobs receive a graceful stop signal before process termination
  • Process cleanup: Local jobs have their PID killed and cleared, with cancelledAt timestamps recorded
  • State persistence: All changes are written to disk immediately so status queries reflect the update
  • Error handling: Attempting to cancel finished, failed, or already-cancelled jobs returns descriptive errors rather than silent failures

Frequently Asked Questions

Can I cancel a Codex job that has already completed?

No. According to the source code in plugins/codex/scripts/lib/job-control.mjs at line 172, the system validates the job state before attempting cancellation. If the job status is completed, failed, or already cancelled, the function throws an error explaining why the operation cannot proceed. This prevents users from accidentally interfering with finalized job records.

What is a "turn interrupt" in Codex job cancellation?

A turn interrupt is a mechanism used for brokered jobs that execute on remote application servers. Before killing the local process handle, the system attempts to send an interrupt signal to the remote executor to stop the current model turn gracefully. As shown in tests/runtime.test.mjs lines 1771–1779, the system tracks whether this attempt was made (turnInterruptAttempted) and whether it succeeded (turnInterrupted), ensuring proper resource cleanup on distributed infrastructure.

Where is the cancellation status persisted?

The cancellation status is persisted to the on-disk state file managed by the job-control module. As demonstrated in tests/runtime.test.mjs lines 1628–1633, the updated job object—including the "cancelled" status, nullified pid, and cancelledAt timestamp—is written to storage immediately after termination. This ensures that the /codex:status command and subsequent operations see the accurate, updated state even if the CLI process restarts.

How can I verify a job was cancelled successfully?

Use the --json flag when cancelling to receive a structured confirmation, or query the job status afterward. A successful cancellation returns a JSON object containing the jobId, status: "cancelled", and turn-interrupt results. If you query status later via /codex:status or the status command, the persistent state file will reflect the cancelled status and the cancelledAt timestamp, confirming the operation completed.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →