Header image for Getting access to the full 1 million token context window in Codex GPT-5.4

Getting access to the full 1 million token context window in Codex GPT-5.4

GPT-5.4 supports a million-token context window but Codex launches it at 272,000.

Codex doesn't ask GPT-5.4 how many tokens it can handle. It looks up the answer in a model catalog -- a JSON file that ships with Codex listing every available model alongside its capabilities: name, context window, supported features. The default catalog sets GPT-5.4's context_window to 272,000. As far as Codex is concerned, that's the ceiling.

Codex profiles let you create named configurations -- a different model, different sandbox rules, different settings -- and switch between them with --profile name. You'd expect to override context_window in a profile. But model_context_window is silently ignored at the profile level. The issue is closed, unresolved. The context window isn't a setting -- it's a model property, defined in the catalog. The profile can't reach into the catalog to change it.

What the profile can do is swap the entire catalog.

The key is model_catalog_json, a profile-scoped path to a custom catalog. Point a profile at a catalog where GPT-5.4's context_window is 1,000,000, and Codex reads that catalog for every session launched under that profile. The default catalog stays untouched. Every other profile -- and the global default -- still reads the original. You're not patching around the system. You're giving it different ground truth to work from.

Building the catalog

Create a directory for custom catalogs:

mkdir --parents ~/.codex/model-catalogs

Export the current catalog and patch GPT-5.4's context window:

codex debug models \\
  | jq '(.models[] | select(.slug == "gpt-5.4") | .context_window) = 1000000' \\
  > ~/.codex/model-catalogs/gpt54-1m-models.json

codex debug models dumps the full model catalog as JSON. The jq filter finds GPT-5.4's entry and changes one field -- context_window -- to 1,000,000. Every other model and every other field is preserved exactly. The result is a complete catalog that differs from the default in a single value.

Wiring the profile

Add the profile to ~/.codex/config.toml:

[profiles.gpt54_1m]
model = "gpt-5.4"
model_catalog_json = "~/.codex/model-catalogs/gpt54-1m-models.json"
model_auto_compact_token_limit = 900000

model_catalog_json tells Codex to read the custom catalog instead of the default when this profile is active. model_auto_compact_token_limit sets the token count at which Codex automatically compacts the conversation -- 900,000 gives room before hitting the million-token ceiling.

Launch with:

codex --profile gpt54_1m

Verifying it works

First, confirm the catalogs diverge. The custom catalog should show 1,000,000; the default should still show 272,000:

jq '.models[] | select(.slug == "gpt-5.4") | {slug, context_window}' \\
  ~/.codex/model-catalogs/gpt54-1m-models.json

codex debug models \\
  | jq '.models[] | select(.slug == "gpt-5.4") | {slug, context_window}'
{ "slug": "gpt-5.4", "context_window": 1000000 }
{ "slug": "gpt-5.4", "context_window": 272000 }

Then confirm Codex uses the right catalog at runtime. Inside the Codex TUI, /status shows the context window size. For a definitive check, inspect the rollout JSON from your session:

latest_rollout=$(
  find ~/.codex/sessions -name 'rollout-*.jsonl' -printf '%T@ %p\\n' \\
    | sort --numeric-sort \\
    | tail --lines=1 \\
    | cut --delimiter=' ' --fields=2-
)

jq -r '
  select(.type == "event_msg" and .payload.type == "task_started")
  | .payload.model_context_window
' "$latest_rollout"

This returns 950,000. GPT-5.4 applies a 95% effective context window internally, so the runtime ceiling is 950,000 of the catalog's 1,000,000.

Keeping the catalog current

The custom catalog is a snapshot of the models available when you built it. When Codex updates and ships new models or changed defaults, the custom catalog won't have them. Regenerate after updates (until the Codex team resolve the issue directly):

codex debug models \\
  | jq '(.models[] | select(.slug == "gpt-5.4") | .context_window) = 1000000' \\
  > ~/.codex/model-catalogs/gpt54-1m-models.json

The tell is when new models stop appearing in sessions launched with the gpt54_1m profile. That means the catalog is stale.