Codex CLI 0.163.0 will print every feature it knows as a key, a stage, and a boolean. The config page tells you to turn a feature off by setting that key to false. The page is describing the file. The process reads a resolved set. On an empty home, eight rows whose stage is removed already have the boolean true. Writing false moved two removed keys this run measured and left three others at true. codex features disable exited 0 for one of the keys that did not move, wrote the file, and printed a success line. The next list still said true.
The useful check is the boolean on the following list, not the stage word and not the success line. A shared config that copies an old false into a key the applicator ignores will look disabled in the file and stay enabled in the process.
What this run used
The binary was Codex CLI 0.163.0 on Linux x86_64, kernel 7.0.14-6-pve, on 11 November 2026. codex --version printed codex-cli 0.163.0. Each measurement used a new CODEX_HOME. The empty measurement had no config.toml. No login was run. codex doctor on those homes exited 1. The failing check was credentials, and the same report also warned that the Responses WebSocket had failed. Those two results are not the feature result. The feature fields were still present under the config check. Doctor also noted that 0.167.2 was available. Nothing in this run was repeated on 0.167.2. Re-list before you treat a key measured here as ignored on a newer binary.
The config page, read the same day, says the [features] table toggles optional and experimental capabilities, and that you disable a feature by setting the key to false. Its common-flag table uses maturity labels such as Experimental, Beta, and Stable, and it says the table is not every internal or under-development feature. The CLI inventory uses a different vocabulary: under development, experimental, stable, deprecated, and removed. sqlite, steer, and terminal_resize_reflow are not in that common table. Absence from the table is not the same as the process ignoring the key.
The registry comment for the removed stage, in the rust-v0.163.0 tag of codex-rs/features/src/lib.rs, says the flag is kept for compatibility and is useless. The list does not print that sentence. The measurements below are what this binary did with the file. The source is the explanation for the split, after the split was observed.
Read the empty inventory before you edit a key
Point CODEX_HOME at a new directory and list the registry. The list does not require a credential.
export CODEX_HOME=/path/to/new-codex-home
codex features list
This binary printed 175 rows: 71 under development, 54 stable, 42 removed, 4 deprecated, and 4 experimental. The stage can be two words. A script that takes the third whitespace field as the boolean will read development on an under-development row. The boolean is the last field.
Eight removed rows were true with no config file:
collaboration_modes
item_ids
resize_all_images
sqlite
steer
terminal_resize_reflow
tool_search_always_defer_mcp_tools
tui_app_server
Four stable rows were false: memories, multi_agent_v2, recommended_plugins, and secret_auth_storage. All four deprecated rows were false: transcript_v2, use_legacy_landlock, web_search_cached, and web_search_request. No experimental row was true. One under-development row was true: instant_interrupt. The stage word is not a default. removed is not off. stable is not on. under development is not off either.
Doctor’s config details on that empty home reported 59 enabled feature flags. That count includes removed rows whose boolean is true. It is the resolved set, not a count of stable flags. If a job stores only the count, it cannot tell a removed-true flag from a stable-true flag. Store the row.
An applied write and an ignored write are both accepted
The same [features] table can contain both kinds of key. This home was given:
[features]
terminal_resize_reflow = false
tool_search_always_defer_mcp_tools = false
item_ids = false
sqlite = false
shell_zsh_fork = true
steer = false
The following list did not echo the file. sqlite and steer were removed and false. shell_zsh_fork was removed and false, which is the empty-home boolean, not the true in the file. terminal_resize_reflow, tool_search_always_defer_mcp_tools, and item_ids were still removed and true.
Doctor on that home reported 57 enabled flags. The override field was sqlite=false, steer=false. The enabled-flag string still contained terminal_resize_reflow, tool_search_always_defer_mcp_tools, item_ids, and tui_app_server. shell_zsh_fork was recorded as a legacy alias, shell_zsh_fork -> shell_zsh_fork, and it was not in the override field and not in the enabled set. The empty-home count was 59. Two applied disables account for the drop to 57. The three ignored false writes did not reduce the count. The written true did not add an override.
That split matches the applicator in the rust-v0.163.0 registry. Before it applies a boolean, it continues past shell_zsh_fork, terminal_resize_reflow, item_ids, tool_search_always_defer_mcp_tools, and tui_app_server. sqlite and steer are not in that continue list, so a false removes them from the resolved set. The stage label on the list is the same word for both groups. The continue list is what decides whether the file wins.
Do not extend that continue list by memory to the other removed-true names. collaboration_modes and resize_all_images were true on the empty list. This run did not write them. tui_app_server stayed true, and the applicator continues past that key, but this run did not put tui_app_server = false in the file. Re-list the key you intend to pin.
A second home, after features disable steer had also been applied, is the human doctor view of the same split. It reported 56 enabled flags and 3 overrides: sqlite, hooks, and steer. hooks was the stable control, set to false in that file, and it moved. The legacy-alias lines were still shell_zsh_fork -> shell_zsh_fork and unified_exec_zsh_fork -> unified_exec_zsh_fork. An alias line means the key was seen and mapped. It does not mean the boolean in the file was applied. unified_exec_zsh_fork = false was in that file and the list stayed false, which was already the default, so that row alone does not prove an ignore. The shell_zsh_fork = true row does, because the list stayed false.
The success line does not name the stage
codex features disable and codex features enable write config.toml and print one sentence. On a home that had no config file, disable of the ignored key did this:
codex features disable terminal_resize_reflow
The process exited 0. The printed line was:
Disabled feature `terminal_resize_reflow` in config.toml.
The file then contained terminal_resize_reflow = false. The next list row was still removed and true.
Enable of the other ignored key did the matching thing:
codex features enable shell_zsh_fork
Exit 0. The printed line was:
Enabled feature `shell_zsh_fork` in config.toml.
The file contained shell_zsh_fork = true, and the list stayed removed and false.
An unknown name is the case the mutator rejects. codex features disable not_a_feature exited 1 and printed Error: Unknown feature flag: not_a_feature. It did not write a row. The mutator knows the registry. It does not know, or at least it does not say, that the applicator will ignore a registered key.
A hand-written unknown key is a third case. not_a_feature = true under [features] stayed in the file. The full doctor report said Codex is ignoring 1 unrecognized configuration setting. Check for typos or deprecated settings. and the following line named features.not_a_feature. The --summary view of that same home did not print that sentence. A job that stores the summary counts will accept a typo the full report already named.
--strict-config is not the substitute. Combined with the features subcommand it exited 1 and printed:
Error: `--strict-config` is not supported for `codex features`
Use the list, then the full doctor report.
An invocation flag is not a file write, and it follows the same split. On a fresh home, codex --disable sqlite features list showed sqlite as removed and false, and it did not create config.toml. codex --disable terminal_resize_reflow features list still showed removed and true. Use the invocation flag when you want one process to try a boolean without persisting it. Then read the list that process printed. If the boolean did not move, persisting the same key with features disable will not move it either.
Verify the boolean after the write
Run the list against the home you mean to change, and keep the row for the key you are about to edit. If the stage is removed, assume nothing about the boolean until you have written the value and listed again. The empty-home defaults above are evidence for 0.163.0, not a table to copy into a newer binary.
For a one-process check, pass --disable or --enable and list again. Confirm config.toml is still absent if you did not intend to persist a change. For a persistent change, run features disable or features enable, then list again without the invocation flag. Accept the edit only when the last field equals the value you wrote. Reject the edit when the success sentence is the only evidence. That sentence is printed for a key the applicator ignores.
Then run doctor without --summary. Require an applied key to appear in the override field, and require the enabled count to move when you disabled a key that had been true. An ignored key stays out of the override field even when the file contains it. A legacy-alias line is a trace that the key was read, not proof that true won. Require the unrecognized-setting sentence to be absent before you commit a hand-edited table. The summary view is the wrong place to look for that sentence.
Do not treat doctor’s exit status as the feature result on a home with no credentials. This run’s doctor exited 1 for that reason, and the feature fields were still readable. Do not start a model session to settle this check. This run did not. The source records a session deprecation notice for the zsh keys, including the sentence that the patched zsh backend has been removed and the setting is ignored. That notice was not captured here, because no session was started. The list boolean and the doctor override set are the checks that do not need a login.
A file that says terminal_resize_reflow = false is not, on this binary, a disabled reflow flag. A file that says shell_zsh_fork = true is not an enabled zsh fork. A file that says sqlite = false does drop sqlite from the resolved set. Copy the row you re-listed, not the sentence the mutator printed, and not the stage word alone.
References
- Codex CLI 0.163.0,
codex --versionoutputcodex-cli 0.163.0, Linux x86_64, kernel 7.0.14-6-pve, 11 November 2026. EachCODEX_HOMEwas a new directory. No login was run.codex doctorexited 1 on those homes because no credentials were present. The feature evidence is thefeatures listrows and the config-check fields, not that exit status. Doctor also reported 0.167.2 available. That release was not installed for this run. - Config basics, feature flags, https://developers.openai.com/codex/config-basic#feature-flags, read 11 November 2026. The page says to disable a feature by setting the key to false, and its common-flag table does not list
sqlite,steer, orterminal_resize_reflow. codex-rs/features/src/lib.rsat tagrust-v0.163.0, https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/features/src/lib.rs. TheRemovedstage comment calls the flag a compatibility leftover. The config applicator continues pastshell_zsh_fork,terminal_resize_reflow,item_ids,tool_search_always_defer_mcp_tools, andtui_app_serverbefore applying a boolean.sqliteandsteerare not in that continue list.