# How to Debug When Job Status Shows Incorrect Information for Codex

> Debug incorrect job status in Codex by addressing persistence failures, async race conditions, or corrupted state files. Resolve stale data now.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: how-to-guide
- Published: 2026-07-18

---

**When the `codex status` command displays stale or wrong data, the issue usually stems from persistence failures, async race conditions, or corrupted state files in the companion process.**

The `openai/codex-plugin-cc` repository implements a job-tracking system that maintains an in-memory **state object** and persists it to disk so the companion process survives restarts. When you need to debug incorrect job status information, you must trace the flow from the API wrapper through the lifecycle controller to the underlying JSON store.

## Understanding the Job Status Architecture

### State Management in `state.mjs`

The global `jobsById` Map lives in `plugins/codex/scripts/lib/state.mjs`. This module exposes the helpers `getJob`, `setJob`, and `updateJobStatus`, which mutate the in-memory state. Every mutation must be followed by `saveState()` to serialize the data to [`.codex/state.json`](https://github.com/openai/codex-plugin-cc/blob/main/.codex/state.json).

### Job Tracking in `tracked-jobs.mjs`

The `TrackedJob` class in `plugins/codex/scripts/lib/tracked-jobs.mjs` wraps raw jobs with **timestamps** and **progress callbacks**. Its `finalize()` method writes the definitive status—`pending`, `running`, `failed`, or `succeeded`—back to the state store.

### Lifecycle Orchestration in `job-control.mjs`

`plugins/codex/scripts/lib/job-control.mjs` contains the central entry points `startJob`, `cancelJob`, and `completeJob`. According to the source code, these functions are the only places that should modify the `status` field.

### API Integration in `codex.mjs`

`plugins/codex/scripts/lib/codex.mjs` provides the thin wrapper around the actual Codex API. It reports success or failure back to the job controller, triggering the status update chain.

## Root Causes of Incorrect Status Reporting

- **State persistence failure**: The companion process crashed before `saveState()` flushed the in-memory map to disk.
- **Async race conditions**: The UI reads the state after the job finishes but before `updateJobStatus` resolves.
- **Corrupted JSON storage**: Malformed data in [`.codex/state.json`](https://github.com/openai/codex-plugin-cc/blob/main/.codex/state.json) causes the loader to fall back to an empty state.
- **Job ID collision**: Manual assignment of job IDs outside the `TrackedJob` constructor can overwrite existing entries.

## Systematic Debugging Steps

1. **Verify the job exists in the current state**:
   ```bash
   codex status --id <job-id>
   ```

   If the ID is missing, the companion never persisted it.

2. **Inspect the raw state file**:
   ```bash
   cat .codex/state.json | jq .
   ```

   Check the `status` field for your target job ID. If the file is empty or the entry is absent, the write operation failed.

3. **Enable verbose companion logging**:
   ```bash
   CODEX_DEBUG=1 codex companion
   ```

   Search the logs for lines containing your job ID and the phrase "updating status" to confirm the lifecycle fired.

4. **Reproduce with a minimal job**:
   ```bash
   codex run --prompt "print('hello')" --output json
   codex status --id <new-job-id>
   ```

   If the status is correct for new jobs, the issue is likely data corruption from previous runs.

5. **Validate the JSON schema**:
   Ensure [`.codex/state.json`](https://github.com/openai/codex-plugin-cc/blob/main/.codex/state.json) conforms to the schema defined in [`plugins/codex/schemas/review-output.schema.json`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/schemas/review-output.schema.json). Invalid JSON will cause the parser to drop the entire state.

6. **Trace the async lifecycle hooks**:
   Add temporary `console.debug` statements inside `setJob` and `updateJobStatus` in `state.mjs`. Restart the companion and watch the logs; the "running → succeeded" sequence should appear without gaps.

7. **Check for duplicate job IDs**:
   Search the codebase for manual `jobId:` assignments outside of `TrackedJob` creation. Duplicate IDs cause one job to overwrite another's status.

8. **Capture unhandled rejections**:
   Run the companion with `node --trace-warnings` to surface promise rejections in `codex.mjs` that abort the update flow, leaving the status stuck at "running".

## Common Fixes and Solutions

**Status stuck on "running" after completion**
- **Root Cause**: The companion process exited before `saveState()` flushed the final state to disk.
- **Fix**: Ensure the companion stays alive until `await runJob()` resolves, or add a `process.on('exit')` guard that calls `saveState()` synchronously.

**Stale data after companion restart**
- **Root Cause**: Corrupted or missing [`.codex/state.json`](https://github.com/openai/codex-plugin-cc/blob/main/.codex/state.json) causes the loader to initialize an empty state.
- **Fix**: Add defensive JSON parsing in `state.mjs` that falls back to an empty Map if `JSON.parse` throws.

**Wrong job ID displayed in status**
- **Root Cause**: The client passed an incorrect `--id` flag, or a manual job ID collided with an existing entry.
- **Fix**: Validate the provided ID against `state.jobsById` keys in `job-control.mjs` before querying, and emit a clear "Job not found" error.

**Inconsistent status between CLI and UI**
- **Root Cause**: A race condition where the status is read between job completion and the `updateJobStatus` promise resolution.
- **Fix**: Serialize status updates using an async lock in `job-control.mjs` to prevent interleaved reads and writes.

## Summary

- The **state object** in `plugins/codex/scripts/lib/state.mjs` is the source of truth, persisted to [`.codex/state.json`](https://github.com/openai/codex-plugin-cc/blob/main/.codex/state.json).
- **Incorrect job status** usually indicates a failure in `saveState()`, async timing issues, or ID collisions in `tracked-jobs.mjs`.
- Debug by inspecting the raw JSON, enabling `CODEX_DEBUG=1` logs, and validating against [`review-output.schema.json`](https://github.com/openai/codex-plugin-cc/blob/main/review-output.schema.json).
- Ensure `TrackedJob.finalize()` and the `job-control.mjs` lifecycle methods are always invoked to prevent stuck states.

## Frequently Asked Questions

### Why does my job status remain "running" after the task completed?

This happens when the companion process exits before `saveState()` persists the final state. Ensure the process stays alive long enough for the `completeJob` promise to resolve, or add a graceful shutdown handler in `job-control.mjs` that flushes pending writes.

### Where is the job state actually stored?

The in-memory state lives in the `jobsById` Map inside `plugins/codex/scripts/lib/state.mjs`, which serializes to [`.codex/state.json`](https://github.com/openai/codex-plugin-cc/blob/main/.codex/state.json) on disk. If this file is corrupted or deleted, the status command defined in [`plugins/codex/commands/status.md`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/commands/status.md) will report empty or stale data.

### How can I verify the state file is not corrupted?

Validate [`.codex/state.json`](https://github.com/openai/codex-plugin-cc/blob/main/.codex/state.json) against the JSON schema defined in [`plugins/codex/schemas/review-output.schema.json`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/schemas/review-output.schema.json) using `ajv-cli` or a Node script. If parsing fails, `state.mjs` should fallback to an empty Map to prevent crashes.

### What causes race conditions in job status updates?

Race conditions occur when `codex status` reads the state between the job completion event in `codex.mjs` and the `updateJobStatus` promise resolution. To fix this, serialize updates using an async lock in `plugins/codex/scripts/lib/job-control.mjs` so reads cannot interleave with writes.