recommended_plugins injection has no working opt-out (feature flag removed / disable-plugins is all-or-nothing)
Summary
The <recommended_plugins> block (list of ~38 not-installed third-party plugins, e.g. Atlassian, Airtable, HubSpot, ...) is unconditionally injected as an input_text item on the first user turn of every session, and there is no working client-side way to suppress it — even though a related feature-flag mechanism exists.
Environment
codex-cli 0.146.0(Windows, native CLI)- Also reproduced earlier on
0.147.0-alpha.6.5
Steps to Reproduce (no API billing — uses local prompt rendering only)
codex debug prompt-input "test"
Output contains a developer/user message with:
"text": "<recommended_plugins>\nHere is a list of plugins that are available but not installed.\n\n- Airtable (airtable@openai-curated-remote)\n- Apollo.io (apollo@openai-curated-remote)\n...\n</recommended_plugins>"
~1,900 characters / ~470 tokens, listing ~38 plugins from a remote/hardcoded catalog (@openai-curated-remote) that is unrelated to the local codex plugin marketplace list configuration.
What I tried (all ineffective)
codex features listno longer even lists arecommended_pluginsflag (it did on the alpha build tested earlier, shown asstable false). Running:
```
codex debug prompt-input --disable recommended_plugins "test"
Error: Unknown feature flag: recommended_plugins
now fails with . On the earlier alpha build where the flag still existed, passing --disable recommended_plugins` produced byte-identical output to the default run (only a random message ID differed) — i.e. the flag never actually controlled this injection path even when it was listed.
--disable plugins(the only remaining related flag,stable trueby default) does remove the<recommended_plugins>block, but it does so by disabling the entire plugin instruction subsystem — it also strips the<plugins_instructions>block that documents currently-installed and enabled plugins. For a user with real plugins enabled (e.g.documents,spreadsheets,chrome,computer-use,pdf, ...), this is not usable: it silences the model's knowledge of plugins actually in use, not just the unused recommendations list.
- Emptying the plugin marketplace config (
-c "marketplaces={}", process-local, non-destructive) does not change the injected list at all — confirming the list is generated from a source independent of local[marketplaces]config.
Impact
Because history_mode: "legacy" resends full conversation history on every API call within a session, this ~470-token block is effectively billed on every turn of the session, not just the first — a constant, unavoidable per-session tax with no opt-out.
Expected behavior
Either:
features.recommended_plugins(or an equivalent, currently-functional flag) actually gates this injection, independent offeatures.plugins, or- the recommendation list is omitted/trimmed after the first turn (respecting
history_mode), or - there is a config key (e.g.
show_recommended_plugins = false) to opt out without disabling real plugin functionality.
Additional notes
- Searched existing issues for
recommended_plugins/ "recommended plugin injection" and found no exact duplicate as of this report. - Happy to provide the full
codex debug prompt-inputoutput (local rendering, no session/user data) if useful.
5 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
yeah same issue here, please fix this annoying issue. I've mentioned it in thid feedback id 01a00523-4b21-76b1-aac9-e44951a2d45b
Root-cause analysis on current
main(I traced the injection path end-to-end). The short version: the bug you're hitting is still present on main today, but the "feature flag removed" part needs a correction — the flag exists now, it just doesn't gate this injection.Status of the flag
features.recommended_pluginswas added on Jul 28 in #35839 ("Decouple recommended plugins from tool suggestions") asStage::Stablewithdefault_enabled: false(codex-rs/features/src/lib.rs:1183). So on current maincodex features listdoes list it, and--disable recommended_pluginsno longer errors with "Unknown feature flag". Your 0.146.0 predates #35839, which explains the "Unknown feature flag" error; your alpha build was the first to carry the flag.Why
--disable recommended_pluginsstill does nothingThe
recommended_pluginsflag is wired up as an OR, not a gate. Inbuild_initial_context_with_world_state(codex-rs/core/src/session/mod.rs:3501):apps,plugins, andtool_suggestare allStage::Stablewithdefault_enabled: true(features/src/lib.rs:1178).recommended_pluginsdefaults tofalse.So with default config, the first turn always satisfies the gate via
tool_suggestalone, and disabling onlyrecommended_pluginscannot suppress the block — exactly the byte-identical output you observed on the alpha. Your--disable pluginsfinding is also consistent:pluginsshort-circuits the entire gate, which is why it also removes the<plugins_instructions>block that documents actually-installed plugins.Where the ~38 entries come from
Confirmed server-side and config-independent, as you suspected.
fetch_recommended_plugins(codex-rs/core-plugins/src/remote.rs:993) does an authenticatedGET {chatgpt_base_url}/ps/plugins/suggested?scope=GLOBALand only falls back to a Legacy mode when the response saysenabled != true(remote.rs:1009) or the request fails — which is also why emptyingmarketplaceslocally has no effect. In Legacy moderecommended_plugin_candidates_for_configreturnsNone(codex-rs/core-plugins/src/manager.rs:1340), so no block is rendered. The rendered list is built byRecommendedPluginsInstructions(codex-rs/core/src/context/recommended_plugins_instructions.rs), capped at 50 entries.The per-turn billing concern
build_initial_context_with_world_stateruns when there is no reference context to diff against — the first turn, after compaction, or on resume. Within a session withhistory_mode: "legacy"the full thread is resent on every API call, so the block is indeed re-billed each turn.Fix directions to consider
Feature::RecommendedPluginsspecifically (not the OR), keepingrequest_plugin_installgated bytool_suggestas it is today (core/src/tools/handlers/request_plugin_install_spec.rs). The flag was added in #35839 specifically to decouple the recommendation list from tool suggestions; as wired, the decoupling only works in one direction (you can enable the list with tool_suggest off, but you cannot disable the list with tool_suggest on).falsefor the flag as a veto: if the feature layer can distinguish "explicitly disabled" from "default-disabled", treatrecommended_plugins = falseas a hard opt-out even whentool_suggestis enabled.For reference, the existing coverage is
codex-rs/app-server/tests/suite/v2/recommended_plugins.rs(both tool_suggest on/off paths assert the block is present), so a fix would need to update those assertions. Happy to help with a proposed patch if any of these directions aligns with the team's intended solution.Comment: openai/codex #38185 — delta from our duplicate #38881
---
Same bug on our side (codex-cli 0.147.0, Linux, app-server mode) — my report
#38881 is a duplicate of this one and is being closed as such. Two observations
not in the report above:
features.*keys placed after a[table]section inconfig.tomlare silently ignored; the same key before the table takeseffect. So "I set
recommended_plugins=falsein config" can fail twice: theflag isn't wired to this injection path, and depending on file layout the
value never parses. Reproduced by moving the key across a
[table]boundaryin the same file (0.147.0).
features.plugins=false(or--disable plugins) removes<recommended_plugins>, but it also removesthe
<plugins_instructions>block that documents actually-installedplugins — confirmed by diffing
codex debug prompt-inputoutput. For userswith real plugins enabled that opt-out is unusable, which is the
"all-or-nothing" point.
Cost anchor: ~1.9 KB ≈ ~0.5K tokens per turn, re-emitted on every turn; with
history_mode: "legacy"the fragment is resent with the full conversation, solong sessions pay it repeatedly.
Found why your flag attempts were ineffective (
main@ 1f41cc5d92). The injection gate is:(
core/src/session/mod.rs#L3505-L3509).recommended_pluginsis default-off — buttool_suggestis default-on (features/src/lib.rs#L1178-L1188), and the||means ToolSuggest alone keeps the block flowing. So-c features.recommended_plugins=falseis a no-op in practice, and the only levers that work are the all-or-nothing ones you found (plugins/appsoff).Working opt-out today:
-c features.tool_suggest=false(withrecommended_pluginsleft at its default false) makes the whole condition false — worth verifying with yourcodex debug prompt-inputcheck, and it also disables tool-suggest generally, so it's still coarser than you asked for.Fix shape: the
||conflates two features. Makerecommended_pluginsthe sole gate for the<recommended_plugins>block (ToolSuggest can imply it as a default, not an override), or honor an explicitrecommended_plugins=falseas a veto. Either restores a targeted opt-out for a ~470-token-per-session injection users can't otherwise control.