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
pidfield is cleared from the job record - The job status is set to "cancelled"
- The
cancelledAttimestamp 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.mjsand flow tojob-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
cancelledAttimestamps 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →