# What Happens When a Codex Turn Is Interrupted Mid-Execution: Inside the OpenAI Codex Plugin

> Discover what happens when an OpenAI Codex turn is interrupted mid-execution. Learn how the app-server handles unfinished work and signals completion.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: internals
- Published: 2026-07-10

---

**When a Codex turn is interrupted mid-execution, the client sends a `turn/interrupt` RPC to the app-server, which aborts pending work by clearing the turn from the `interruptibleTurns` map and returning a `turn/completed` message with the status set to `interrupted`.**

Interrupting a running AI task requires precise coordination between client and server to prevent resource leaks and ensure state consistency. In the **openai/codex-plugin-cc** repository, the Codex runtime implements a cooperative interruption protocol that allows clients to abort specific turns programmatically or via CLI commands. This mechanism relies on thread and turn identifiers to precisely target running operations without affecting other concurrent tasks.

## The Client-Side Interrupt Request

The interruption flow begins in `plugins/codex/scripts/lib/codex.mjs`, where the `interruptAppServerTurn` function handles the outgoing request. This helper first validates that both a `threadId` and `turnId` are provided and that the Codex runtime is available in the current working directory.

If validation passes, the function opens a persistent broker client and issues a `turn/interrupt` RPC to the running Codex app-server. The method returns an object describing whether the interrupt was *attempted* and whether it ultimately succeeded, allowing calling code to handle failure cases gracefully.

According to the source code at lines 60-89, the function signature requires the target working directory and an options object containing the thread and turn identifiers:

```javascript
// From plugins/codex/scripts/lib/codex.mjs
async function interruptAppServerTurn(cwd, { threadId, turnId }) {
  // Validation and RPC logic
}

```

## App-Server Processing and Turn Abort

When the app-server receives the `turn/interrupt` method, it processes the request by looking up the turn in an internal `interruptibleTurns` map. As implemented in `tests/fake-codex-fixture.mjs` (lines 11-31), the handler verifies that the turn exists and is eligible for interruption.

If the turn is found, the server immediately clears any pending timeout associated with that turn, removes the entry from the `interruptibleTurns` map to prevent duplicate processing, and dispatches a `turn/completed` message back to the client. This response explicitly marks the turn as `interrupted`, distinguishing it from natural completion or error states.

The test fixture demonstrates this logic at lines 24-30:

```javascript
// Simplified from tests/fake-codex-fixture.mjs
if (turn) {
  clearTimeout(turn.timeout);
  interruptibleTurns.delete(turnId);
  send({ method: 'turn/completed', turn: { ...turn, status: 'interrupted' } });
}

```

## Result Propagation and User Feedback

Once the app-server responds, the client code evaluates the result. The `interruptAppServerTurn` function returns an object containing `attempted` and `interrupted` boolean flags. If the RPC succeeded, both values are `true`, confirming that the server acknowledged and processed the interruption.

In `plugins/codex/scripts/codex-companion.mjs` (lines 976-982), the companion script consumes this result to provide user-facing feedback. Upon successful interruption, it logs a message such as "Requested Codex turn interrupt for `<turnId>` on `<threadId>`," ensuring operators can track which operations were manually terminated.

## CLI Integration: The Cancel Command

End users typically trigger interruptions through the `codex cancel` command, documented in [`plugins/codex/commands/cancel.md`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/commands/cancel.md). This CLI wrapper ultimately invokes the same `interruptAppServerTurn` function, translating command-line arguments for thread and turn IDs into the RPC mechanism described above.

The integration is verified in `tests/runtime.test.mjs` (lines 1740-1744), where test cases confirm that issuing a cancel command sends the interrupt request before terminating the brokered task. This ensures that the interruption signal propagates to the server even when the client process is shutting down.

## Programmatic Implementation Examples

Developers can integrate turn interruption into custom workflows using the Codex plugin library.

### Programmatic Interrupt

To interrupt a turn from JavaScript, import `interruptAppServerTurn` and await the result:

```javascript
import { interruptAppServerTurn } from './plugins/codex/scripts/lib/codex.mjs';

const cwd = process.cwd();
const result = await interruptAppServerTurn(cwd, {
  threadId: 'thread-123',
  turnId: 'turn-456'
});

if (result.interrupted) {
  console.log('Turn was successfully interrupted.');
} else if (result.attempted) {
  console.warn('Interrupt was attempted but failed:', result.detail);
} else {
  console.warn('Interrupt not attempted:', result.detail);
}

```

### Using the CLI Cancel Command

For command-line usage, specify the thread and turn IDs as arguments:

```bash
codex cancel --thread-id thread-123 --turn-id turn-456

# → Requested Codex turn interrupt for turn-456 on thread-123.

```

### Handling Interrupts in Client Scripts

When building interactive applications, you can capture a turn reference and interrupt it later based on user input:

```javascript
import { startThread, captureTurn } from './plugins/codex/scripts/lib/codex.mjs';

async function runAndPossiblyInterrupt() {
  const client = await CodexAppServerClient.connect(cwd, { reuseExistingBroker: true });
  const thread = await startThread(client, cwd, { model: 'gpt-4', sandbox: 'read-only' });
  const turn = await captureTurn(client, thread.thread.id, () => client.request('some/long-running-task'));
  
  // If user cancels:
  await interruptAppServerTurn(cwd, { 
    threadId: thread.thread.id, 
    turnId: turn.turnId 
  });
}

```

## Summary

- **Cooperative Protocol**: Interrupting a Codex turn requires the client to send a `turn/interrupt` RPC and the server to acknowledge it by removing the turn from the `interruptibleTurns` map.
- **Validation Required**: The `interruptAppServerTurn` function in `plugins/codex/scripts/lib/codex.mjs` validates `threadId` and `turnId` before attempting the RPC.
- **Server-Side Cleanup**: The app-server clears pending timeouts and sends a `turn/completed` message with `status: 'interrupted'` to confirm the operation.
- **CLI and Programmatic Access**: Both the `codex cancel` command and direct JavaScript API calls use the same underlying interruption mechanism.
- **Graceful Degradation**: If a turn is not found in `interruptibleTurns`, the interrupt request is ignored and the turn continues to completion.

## Frequently Asked Questions

### What happens if I try to interrupt a Codex turn that has already completed?

If the turn is not present in the app-server's `interruptibleTurns` map—either because it finished naturally or was already interrupted—the server ignores the request. The client receives a response indicating the interrupt was attempted but the turn continues to completion without interruption.

### Can I interrupt a Codex turn from a different process than the one that started it?

Yes, provided the interrupting process has access to the same Codex runtime broker and knows the correct `threadId` and `turnId`. The `interruptAppServerTurn` function opens a persistent broker client to send the RPC, allowing cross-process interruption as long as the broker is accessible.

### What is the difference between `attempted` and `interrupted` in the return value?

The `attempted` field indicates whether the client successfully sent the `turn/interrupt` RPC to the app-server, while `interrupted` confirms that the server actually found and terminated the turn. If `attempted` is `true` but `interrupted` is `false`, the server received the request but the turn was not in an interruptible state.

### How does the `codex cancel` command relate to the interrupt API?

The `codex cancel` command documented in [`plugins/codex/commands/cancel.md`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/commands/cancel.md) is a CLI wrapper that calls `interruptAppServerTurn` internally. It translates command-line arguments into the RPC mechanism, making the programmatic interruption API accessible from the command line without requiring custom JavaScript code.