Autocomplete “no match” menu intercepts Enter for ! shell commands

Open 💬 2 comments Opened Jul 23, 2026 by HyuN-wwy

What version of Codex CLI is running?

Codex CLI 0.145.0

What subscription do you have?

Chat GPT pro

Which model were you using?

gpt 5.6 Sol

What platform is your computer?

Microsoft Windows NT 10.0.26200.0 x64

What terminal emulator and version are you using (if applicable)?

Windows Powershell

Codex doctor report

{
  "schemaVersion": 1,
  "generatedAt": "1784769061s since unix epoch",
  "overallStatus": "ok",
  "codexVersion": "0.145.0",
  "checks": {
    "app_server.status": {
      "id": "app_server.status",
      "category": "app-server",
      "status": "ok",
      "summary": "background server is not running",
      "details": {
        "control socket": "C:\\Users\\HyuN\\.codex\\app-server-control\\app-server-control.sock",
        "daemon state dir": "C:\\Users\\HyuN\\.codex\\app-server-daemon",
        "mode": "ephemeral",
        "pid file": "C:\\Users\\HyuN\\.codex\\app-server-daemon\\app-server.pid (missing)",
        "settings": "C:\\Users\\HyuN\\.codex\\app-server-daemon\\settings.json (missing)",
        "status": "not running",
        "update-loop pid file": "C:\\Users\\HyuN\\.codex\\app-server-daemon\\app-server-updater.pid (missing)"
      },
      "remediation": null,
      "durationMs": 0
    },
    "auth.credentials": {
      "id": "auth.credentials",
      "category": "auth",
      "status": "ok",
      "summary": "auth is configured",
      "details": {
        "auth file": "C:\\Users\\HyuN\\.codex\\auth.json",
        "auth storage mode": "File",
        "stored API key": "false",
        "stored ChatGPT tokens": "true",
        "stored agent identity": "false",
        "stored auth mode": "chatgpt"
      },
      "remediation": null,
      "durationMs": 0
    },
    "config.load": {
      "id": "config.load",
      "category": "config",
      "status": "ok",
      "summary": "config loaded",
      "details": {
        "CODEX_HOME": "C:\\Users\\HyuN\\.codex",
        "config.toml": "C:\\Users\\HyuN\\.codex\\config.toml",
        "config.toml parse": "ok",
        "cwd": "D:\\codex\\snake",
        "enabled feature flags": "<redacted>",
        "feature flag overrides": "none",
        "feature flags enabled": "36",
        "log dir": "C:\\Users\\HyuN\\.codex\\log",
        "mcp servers": "0",
        "model": "<default>",
        "model provider": "openai",
        "sqlite home": "C:\\Users\\HyuN\\.codex"
      },
      "remediation": null,
      "durationMs": 0
    },
    "git.environment": {
      "id": "git.environment",
      "category": "git",
      "status": "ok",
      "summary": "git version 2.53.0.windows.2",
      "details": {
        ".git entry": "directory",
        "PATH git #1": "C:\\Program Files\\Git\\cmd\\git.exe",
        "PATH git entries": "1",
        "git build options": "git version 2.53.0.windows.2; cpu: x86_64; built from commit: e9edee0b34751bf4d7d1feda0e2535bff64d4e77; sizeof-long: 4; sizeof-size_t: 8; shell-path: D:/git-sdk-64/usr/bin/sh; rust: disabled; feature: fsmonitor--daemon; gettext: enabled; libcurl: 8.18.0; OpenSSL: OpenSSL 3.5.5 27 Jan 2026; zlib: 1.3.1; SHA-1: SHA1_DC; SHA-256: SHA256_BLK; default-ref-format: files; default-hash: sha1",
        "git exec path": "C:/Program Files/Git/mingw64/libexec/git-core",
        "git version": "git version 2.53.0.windows.2",
        "repo detected": "true",
        "repo root": "D:\\codex\\snake",
        "selected git": "C:\\Program Files\\Git\\cmd\\git.exe"
      },
      "remediation": null,
      "durationMs": 66
    },
    "installation": {
      "id": "installation",
      "category": "install",
      "status": "ok",
      "summary": "installation looks consistent",
      "details": {
        "PATH codex #1": "C:\\Users\\HyuN\\AppData\\Roaming\\npm\\codex",
        "PATH codex #2": "C:\\Users\\HyuN\\AppData\\Roaming\\npm\\codex.cmd",
        "PATH codex entries": "2",
        "current executable": "C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc\\bin\\codex.exe",
        "install context": "npm (package C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc, bin C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc\\bin, resources C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc\\codex-resources, path C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc\\codex-path)",
        "managed by bun": "false",
        "managed by npm": "true",
        "managed by pnpm": "false",
        "managed package root": "C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex",
        "npm update target": "C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex"
      },
      "remediation": null,
      "durationMs": 217
    },
    "mcp.config": {
      "id": "mcp.config",
      "category": "mcp",
      "status": "ok",
      "summary": "no MCP servers configured",
      "details": {},
      "remediation": null,
      "durationMs": 0
    },
    "network.env": {
      "id": "network.env",
      "category": "network",
      "status": "ok",
      "summary": "network-related environment looks readable",
      "details": {
        "proxy env vars present": "HTTP_PROXY, HTTPS_PROXY, http_proxy, https_proxy"
      },
      "remediation": null,
      "durationMs": 0
    },
    "network.provider_reachability": {
      "id": "network.provider_reachability",
      "category": "reachability",
      "status": "ok",
      "summary": "active provider endpoints are reachable over HTTP",
      "details": {
        "ChatGPT base URL": "https://chatgpt.com/backend-api/ reachable (HTTP 403)",
        "reachability mode": "ChatGPT auth"
      },
      "remediation": null,
      "durationMs": 748
    },
    "network.websocket_reachability": {
      "id": "network.websocket_reachability",
      "category": "websocket",
      "status": "ok",
      "summary": "Responses WebSocket handshake succeeded",
      "details": {
        "DNS": "1 IPv4, 1 IPv6, first IPv6",
        "auth mode": "chatgpt",
        "connect timeout": "15000 ms",
        "endpoint": "wss://chatgpt.com/backend-api/<redacted>",
        "handshake result": "HTTP 101 Switching Protocols",
        "model provider": "openai",
        "models etag present": "true",
        "provider name": "OpenAI",
        "proxy env vars present": "HTTP_PROXY, HTTPS_PROXY, http_proxy, https_proxy",
        "reasoning header": "false",
        "server model present": "false",
        "supports websockets": "true",
        "wire API": "responses"
      },
      "remediation": null,
      "durationMs": 1879
    },
    "runtime.provenance": {
      "id": "runtime.provenance",
      "category": "runtime",
      "status": "ok",
      "summary": "running npm on windows-x86_64",
      "details": {
        "commit": "unknown",
        "current executable": "C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc\\bin\\codex.exe",
        "install method": "npm (package C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc, bin C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc\\bin, resources C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc\\codex-resources, path C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc\\codex-path)",
        "platform": "windows-x86_64",
        "version": "0.145.0"
      },
      "remediation": null,
      "durationMs": 0
    },
    "runtime.search": {
      "id": "runtime.search",
      "category": "search",
      "status": "ok",
      "summary": "search is OK (bundled)",
      "details": {
        "search command": "C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\node_modules\\@openai\\codex-win32-x64\\vendor\\x86_64-pc-windows-msvc\\codex-path\\rg.exe",
        "search command readiness": "file exists",
        "search provider": "bundled"
      },
      "remediation": null,
      "durationMs": 0
    },
    "sandbox.helpers": {
      "id": "sandbox.helpers",
      "category": "sandbox",
      "status": "ok",
      "summary": "sandbox configuration is readable",
      "details": {
        "approval policy": "OnRequest",
        "codex-linux-sandbox helper": "none",
        "execve wrapper helper": "none",
        "filesystem sandbox": "restricted",
        "network sandbox": "restricted"
      },
      "remediation": null,
      "durationMs": 0
    },
    "state.paths": {
      "id": "state.paths",
      "category": "state",
      "status": "ok",
      "summary": "state paths and databases are inspectable",
      "details": {
        "CODEX_HOME": "C:\\Users\\HyuN\\.codex (dir)",
        "active rollout files": "19 files, 15312689 total bytes, 805931 average bytes",
        "archived rollout files": "0 files, 0 total bytes, 0 average bytes",
        "goals DB": "C:\\Users\\HyuN\\.codex\\goals_1.sqlite (file)",
        "goals DB integrity": "ok",
        "log DB": "C:\\Users\\HyuN\\.codex\\logs_2.sqlite (file)",
        "log DB integrity": "ok",
        "log dir": "C:\\Users\\HyuN\\.codex\\log (missing)",
        "memories DB": "C:\\Users\\HyuN\\.codex\\memories_1.sqlite (file)",
        "memories DB integrity": "ok",
        "sqlite home": "C:\\Users\\HyuN\\.codex (dir)",
        "state DB": "C:\\Users\\HyuN\\.codex\\state_5.sqlite (file)",
        "state DB integrity": "ok",
        "thread history DB": "C:\\Users\\HyuN\\.codex\\thread_history_1.sqlite (missing)",
        "thread history DB integrity": "skipped (missing)"
      },
      "remediation": null,
      "durationMs": 122
    },
    "state.rollout_db_parity": {
      "id": "state.rollout_db_parity",
      "category": "threads",
      "status": "ok",
      "summary": "rollout files and state DB thread inventory agree",
      "details": {
        "default model provider": "openai",
        "rollout DB active files": "19",
        "rollout DB active rows": "19",
        "rollout DB archive mismatches": "0",
        "rollout DB archived files": "0",
        "rollout DB archived rows": "0",
        "rollout DB duplicate DB paths": "0",
        "rollout DB duplicate rollout thread ids": "0",
        "rollout DB malformed file names": "0",
        "rollout DB missing active rows": "0",
        "rollout DB missing archived rows": "0",
        "rollout DB model providers": "openai=19",
        "rollout DB rows": "19",
        "rollout DB scan cap reached": "false",
        "rollout DB scan errors": "0",
        "rollout DB sources": "cli=16, subagent:review=2, vscode=1",
        "rollout DB stale rows": "0"
      },
      "remediation": null,
      "durationMs": 53
    },
    "system.environment": {
      "id": "system.environment",
      "category": "system",
      "status": "ok",
      "summary": "OS language zh-CN",
      "details": {
        "EDITOR": "not set",
        "VISUAL": "not set",
        "os": "Windows 10.0.26200 (Windows 11 CoreCountrySpecific) [64-bit]",
        "os language": "zh-CN",
        "os type": "Windows",
        "os version": "10.0.26200"
      },
      "remediation": null,
      "durationMs": 0
    },
    "terminal.env": {
      "id": "terminal.env",
      "category": "terminal",
      "status": "ok",
      "summary": "terminal metadata was detected",
      "details": {
        "WT_SESSION": "present",
        "color output": "disabled (stdout is not a terminal)",
        "console input code page": "936",
        "console output code page": "936",
        "stderr console mode": "unavailable",
        "stderr is terminal": "false",
        "stdin is terminal": "false",
        "stdout console mode": "unavailable",
        "stdout is terminal": "false",
        "terminal": "Windows Terminal",
        "terminal size": "120x30"
      },
      "remediation": null,
      "durationMs": 0
    },
    "terminal.title": {
      "id": "terminal.title",
      "category": "title",
      "status": "ok",
      "summary": "terminal title default",
      "details": {
        "terminal title activity": "true",
        "terminal title items": "activity, project-name",
        "terminal title project source": "git repo root",
        "terminal title project value": "snake",
        "terminal title source": "default"
      },
      "remediation": null,
      "durationMs": 0
    },
    "updates.status": {
      "id": "updates.status",
      "category": "updates",
      "status": "ok",
      "summary": "update configuration is locally consistent",
      "details": {
        "cached latest version": "0.145.0",
        "check for update on startup": "true",
        "last checked at": "2026-07-22T07:31:02.891754500Z",
        "latest version": "0.145.0",
        "latest version status": "current version is not older",
        "npm update target": "C:\\Users\\HyuN\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex",
        "update action": "npm install -g @openai/codex",
        "version cache": "C:\\Users\\HyuN\\.codex\\version.json"
      },
      "remediation": null,
      "durationMs": 1903
    }
  }
}

What issue are you seeing?

In Codex CLI on Windows, entering a shell command such as:

!echo $env:HTTP_PROXY

opens the autocomplete menu and displays “no match”. While this menu is open,
pressing Enter does nothing—the command is not executed.

It appears that the autocomplete menu intercepts Enter as a candidate-selection
action even though there are no candidates. Moving the cursor back near the
leading “!” changes the menu state, after which Enter executes the command
normally.

What steps can reproduce the bug?

Uploaded thread: 019f8aae-062e-7a52-9378-cf0d9f25275c

What is the expected behavior?

When a prompt starts with “!”, Codex CLI should recognize it as a shell command.
If the autocomplete menu has no matching candidates, it should close or stop
capturing keyboard input.

Pressing Enter should submit and execute the shell command without requiring the
cursor to be moved back near the leading “!” character.

Additional information

_No response_

View original on GitHub ↗

2 Comments

bbingz · 1 month ago

Analysis (community)

Thanks for the clear repro - the symptoms line up tightly with the composer's skill-popup key handling.

  • Environment / versions: Reporter: Codex CLI 0.145.0, Windows, Windows Terminal. Analysis against current main (codex-rs/tui, static read only; not a live Windows re-run on my side).
  • Repro status: Partial / code-level confirmation. I did not re-drive the TUI on Windows, but the reported path (!echo $env:HTTP_PROXY -> "no matches" menu -> Enter does nothing; moving the cursor near the leading ! clears the menu and Enter works) matches the control flow below. Happy to correct anything if maintainers see a different popup path (e.g. mentions v2).
  • Observed vs expected:
  • Observed: While the empty skill autocomplete ("no matches") is open over $env:…, plain Enter neither inserts nor submits; the draft stays put.
  • Expected: Enter with no selection should submit the ! shell draft (same contract as file / unified-mention popups when nothing is selected).

Root-cause hypothesis

Two cooperating pieces, both in codex-rs/tui:

  1. Trigger - PowerShell-style $env:… is treated as a completable $ skill query
  • completion_target::dollar_query_kind (codex-rs/tui/src/bottom_pane/chat_composer/completion_target.rs) classifies a name as ShellVariable only when it is all non-lowercase and is_common_env_var (codex-rs/tui/src/mention_codec.rs: PATH / HOME / USER / … only).
  • For $env:HTTP_PROXY, the name segment includes lowercase env (and : is allowed in the name scan), so it falls through to DollarQueryKind::Completable rather than shell syntax.
  • With skills/plugins/connectors loaded, ChatComposer::current_mention_target + sync_mention_popup open ActivePopup::Skill. Empty filter render is codex-rs/tui/src/bottom_pane/skill_popup.rs -> render_rows_single_line(..., "no matches").
  • Existing tests intentionally keep skill-style names like home:search completable (completion_target_tests.rs::dollar_query_classifies_shell_and_skill_syntax), so a blunt "no colons" rule would be wrong.
  1. Direct bug - skill popup Enter with no selection does not fall through to submit
  • Dispatch: ChatComposer::handle_key_event -> ActivePopup::Skill -> handle_key_event_with_skill_popup, then always sync_popups().
  • On plain Enter / Tab, handle_key_event_with_skill_popup optionally inserts selected_mention(), sets active = None, and returns (InputResult::None, true) - no call to handle_key_event_without_popup.
  • With no selection, Enter therefore only closes; sync_popups() immediately reopens ActivePopup::Skill because the $… token is still active -> Enter appears to do nothing.
  • Cursor movement off the $ token (e.g. toward ! / start of the shell draft) clears the target, so Enter can submit - matches the report.

Contrast (intended contract already elsewhere):

  • handle_key_event_with_file_popup: no selection + Enter -> active = None then return self.handle_key_event_without_popup(key_event); Tab stays close-only.
  • handle_key_event_with_mentions_v2_popup: Enter sets submit_without_popup = selected.is_none() and, after close, return self.handle_key_event_without_popup(key_event).
  • Tests document that contract: enter_submits_when_file_popup_has_no_selection and enter_submits_when_unified_mention_popup_has_no_selection in codex-rs/tui/src/bottom_pane/chat_composer.rs. There is no skill-popup counterpart.

Slash-command popups are already gated with !self.draft.is_bash_mode in sync_popups; the legacy $ skill path is not, which is why ! shell drafts still surface skill autocomplete.

High-level fix outline (not a PR)

Primary (small, preferred): In handle_key_event_with_skill_popup, on plain Enter when selected_mention() is None, set active = None and return self.handle_key_event_without_popup(key_event) - same contract as File / MentionV2 - so ! shell drafts (and ordinary prompts with an empty skill menu) submit. Keep Tab as close-only without submit if that remains existing Tab semantics for this popup.

Optional secondary (separate change, needs product alignment):

  • Teach dollar_query_kind that PowerShell-style $env: / $Env: prefixes are non-completable shell syntax (narrow prefix rule), without breaking intentional skill names that use : (e.g. home:search).
  • Optionally suppress $ skill popups while draft.is_bash_mode (parallel to slash-popup bash gating).

Non-goals / risks: Do not change File / MentionV2 fallthrough, slash-popup bash gating, or RunUserShellCommand submission itself. Enter fallthrough looks low-risk and aligns with existing tests. Classification changes must not hide real skills; bash-mode popup suppression is a product call more than a pure bugfix.

Test ideas

Fail-first unit tests next to the existing file/mention_v2 cases in codex-rs/tui/src/bottom_pane/chat_composer.rs:

  1. enter_submits_when_skill_popup_has_no_selection - set_skill_mentions with unrelated skills, insert $env:HTTP_PROXY or $zzzz-no-skill, sync_popups asserts ActivePopup::Skill, Enter -> InputResult::Submitted with unchanged draft text.
  2. enter_submits_bang_shell_command_when_skill_popup_empty - same idea with a leading ! / bash-mode draft matching the reporter command.
  3. If classification is tightened: assert dollar_query_kind("env:HTTP_PROXY") is not completable and that token does not open a skill popup; keep home:search completable.

Questions for maintainers

  • Does the primary Enter fallthrough (mirror File / MentionV2) match the intended skill-popup contract, or is empty skill menu meant to block submit until Esc?
  • Should $ skill completion be disabled entirely in is_bash_mode (like slash commands), or only fixed at the Enter fallthrough layer?
  • Is a narrow $env: / $Env: shell-syntax exception acceptable given skill names may use :?
  • Any Windows-only Enter modifier quirks (KeyModifiers != NONE) worth treating as a secondary path, or is the plain NONE path sufficient given the reporter's "no matches" + cursor-move behavior?
  • For the original report: were skills/plugins loaded (required for mentions_enabled -> Skill popup)? Was mentions v2 in play (would prefer @ paths, not $ skill)?

Happy to refine this analysis with more pointers or adjust the outline if any of the above is off. If the team later wants an invited PR along an agreed approach, I'd be glad to help - no rush either way.

bbingz · 1 month ago

Building on the root-cause analysis above (which already traced the skill-popup Enter close-without-submit and the File/MentionV2 fallthrough), here is the commit archaeology pinning the divergence and current line pins. Traced against main as of 5dd992a (2026-07-24).

What #23363 changed, precisely - and what it did not. git log -S "submit_without_popup" -- codex-rs/tui/src/bottom_pane/chat_composer.rs returns a single commit: 6c1215dac / #23363 (2026-05-28, "TUI: Unified mentions tweaks + polish mentions rendering"). #23363 introduced the submit_without_popup fallthrough in handle_key_event_with_mentions_v2_popup so Enter with no selection submits the draft instead of just closing the popup. The file popup already had Enter-fallthrough before #23363: the parent 6c1215dac^ still has let Some(sel) = popup.selected_match() else { ... self.handle_key_event_without_popup(key_event) } (then at chat_composer.rs:1820-1823). #23363 did not change the skill popup's Enter/no-selection handling (it added only a doc comment to handle_key_event_with_skill_popup).

Reframing - #23363 did not introduce the inconsistency; it elevated MentionV2. The skill popup handler has always closed-on-Enter-without-submitting. It was introduced in 9a50a0440 / #7506 ("Support listing and selecting skills via $ or /skills") and later moved/renamed via #14717 / #15922 / #16104. The file popup has had Enter-fallthrough for longer than #23363. So the skill popup was already inconsistent with the file popup before #23363; #23363 added the same fallthrough to MentionV2, leaving the skill popup as the remaining incomplete port among the File/Skill/MentionV2 token-completion siblings. (The slash/command popup also falls through on Enter with no selection, so this "outlier" scope is File/Skill/MentionV2.) The bug is latent until mentions/skills are enabled so ActivePopup::Skill opens.

Line pins (as of 5dd992a), still unfixed. handle_key_event_with_skill_popup is at chat_composer.rs:2063; its Tab|Enter arm (:~2113-2126) on no selection does close_popup = true; (InputResult::None, true) and never calls handle_key_event_without_popup. The file popup's contract to mirror is at chat_composer.rs:2040-2046 (self.popups.active = ActivePopup::None; return if key_event.code == KeyCode::Enter { self.handle_key_event_without_popup(key_event) } else { (InputResult::None, true) }). The no-selection branch of the skill popup's Tab|Enter arm is the one-region fix target.

Not fixed on main. The only commit touching chat_composer.rs since the issue date (2026-07-23) is 5b40227 / #35012 "Expose remote skill icon URLs through app server" (icon-URL plumbing, not Enter handling). skill_popup.rs's most recent commit is #33232 (2026-07-15, "Disambiguate skill mentions from shell parameters" - before the report, and unrelated to Enter handling).