Windows PowerShell parses git @{u} upstream shorthand as a hashtable literal
Bug: git rev-parse ... @{u} fails on Windows because PowerShell parses @{u} as a hashtable literal
Summary
On Windows with PowerShell as the shell, Codex's tool router runs
git rev-parse --abbrev-ref --symbolic-full-name @{u}
by handing it to PowerShell as a command string. PowerShell sees @{ and tries
to parse @{u} as a hashtable literal, which fails before git is ever
invoked:
At line:2 char:52
+ git rev-parse --abbrev-ref --symbolic-full-name @{u}
+ ~
Missing '=' operator after key in hash literal.
The hash literal was incomplete.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingEqualsInHashLiteral
The command exits 1 and the upstream-branch lookup never runs. In git, @{u}
(a.k.a. @{upstream}) is the revision shorthand for "the upstream of the current
branch" - a perfectly valid git argument that PowerShell mangles because @{...}
is hashtable syntax in PowerShell.
Environment
- OS: Windows 10 Pro (10.0.19045)
- Shell: Windows PowerShell 5.1 (5.1.19041.7417) - also reproduces on
PowerShell 7, since @{ is hashtable syntax in all PowerShell editions
- Codex CLI: 0.142.2
- ChatGPT VS Code extension: 26.623.42026 (win32-x64)
Actual log line (from the Codex output channel)
[error] [CodexMcpConnection] cli: message="... ERROR codex_core::tools::router:
error=Exit code: 1
Wall time: 0.4 seconds
Output:
At line:2 char:52
+ git rev-parse --abbrev-ref --symbolic-full-name @{u}
+ ~
Missing '=' operator after key in hash literal.
At line:2 char:52
+ git rev-parse --abbrev-ref --symbolic-full-name @{u}
+ ~
The hash literal was incomplete.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingEqualsInHashLiteral"
Steps to reproduce
- On Windows, with PowerShell as the active shell, open a git repository whose
current branch has an upstream configured.
- Use Codex in a flow that triggers its branch/upstream detection (it runs
git rev-parse --abbrev-ref --symbolic-full-name @{u} internally; observed via
turn-diff / branch-status capture).
- Open the Codex output channel and observe the
ParserErrorabove.
Minimal standalone repro of the underlying cause (no Codex needed):
# FAILS - PowerShell parses @{u} as a hashtable literal:
git rev-parse --abbrev-ref --symbolic-full-name @{u}
# WORKS - quoting stops PowerShell from parsing the braces:
git rev-parse --abbrev-ref --symbolic-full-name '@{u}'
# WORKS - stop-parsing token passes the rest verbatim:
git --% rev-parse --abbrev-ref --symbolic-full-name @{u}
Expected behavior
git rev-parse --abbrev-ref --symbolic-full-name @{u} should run and return the
upstream branch (e.g. origin/main), regardless of which shell Codex uses on
Windows.
Root cause
Codex builds the git invocation as a PowerShell command string, so the@{u} argument is subject to PowerShell's parser. @{ ... } is PowerShell's
hashtable-literal syntax; @{u} has a key with no =, so PowerShell raises aParserError (MissingEqualsInHashLiteral) and never spawns git. This is a
shell-quoting / argument-passing issue specific to the Windows PowerShell path -
on bash/zsh the same string is passed through to git unharmed.
Suggested fixes (any one resolves it)
- Pass argv directly, bypassing shell parsing. Spawn
gitwith an argument
array (e.g. Start-Process/CreateProcess-style argv, or git.exe invoked
with discrete args) instead of composing one PowerShell command string. This
is the most robust fix and avoids an entire class of PowerShell-quoting bugs.
- Quote the revision argument when targeting PowerShell:
... --symbolic-full-name '@{u}'.
- Use the PowerShell stop-parsing token
--%before the git args so the
remainder is passed verbatim: git --% rev-parse ... @{u}.
Option 1 is preferred because other git revision/argument forms (HEAD@{0},@{push}, paths/globs with special chars) can hit similar PowerShell-parsing
pitfalls.
Impact / severity
Low severity, but persistent and Windows-specific:
- Affects only the Windows + PowerShell path; macOS/Linux are unaffected
(which is likely why it has survived frequent extension updates - the
PowerShell path is under-tested).
- It fails a non-critical background helper (upstream-branch / turn-diff
capture), so the user's primary command still works; the failure surfaces only
as a log error, generating little user-facing signal.
- Net effect: noisy, repeated
ParserErrorin the Codex output channel on every
affected turn, and broken upstream-branch detection for Windows PowerShell
users.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗