fix(api): refuse a shell-mangled path instead of sending it as a 404 - #19
Merged
Conversation
Under Git Bash on Windows, `reply api /v3/whoami` never reached the CLI as typed. MSYS converts a leading-slash argument into a Windows path, so the request went to https://api.reply.io/C:/Program Files/Git/v3/whoami and came back 404 — indistinguishable from an endpoint that does not exist. Quoting does not help; quotes are removed before the conversion. That cost real damage, not just a detour. An agent exploring the product hit it on the prospect-search endpoints and told its user the feature "returns 404 on this account, so the beta isn't enabled for team 18185". The feature works on that account. An environment quirk became a confident, wrong statement about someone's own entitlements. One cause, not two. A dummy query string appeared to fix it only because a `?` stops MSYS treating the argument as a path. The path is now checked before anything is spent: it must start with `/`. A drive-prefixed path is recognised as shell mangling, the intended path is recovered from it so the fix can be quoted back verbatim rather than described, and the error names MSYS, says that quoting will not help, and gives the two remedies that do. Exit is 2 (usage), so a mangled path can never be mistaken for an upstream 404, which exits 1. PowerShell, cmd, macOS and Linux see no change. Found while fixing it: the top-level handler printed only `error.message` and dropped `hint` entirely, so every hint on a UsageError or RuntimeError was written and never seen — 47 of them, including the one that explains how to switch team. Api_error was unaffected because it bakes its hint into the message. Hints now print for all three, in Api_error's existing ` Hint: ...` shape, with continuation lines keeping their indentation so a quoted command stands out. `--json` output is unchanged; the hint was already in `to_json`.
vigubikReply
approved these changes
Aug 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Under Git Bash / MSYS on Windows,
reply api /v3/whoaminever reached the CLI as typed. MSYSconverts a leading-slash argument into a Windows path, so the request went out as:
A 404 that reads exactly like an endpoint which does not exist. Quoting does not help — quotes
are removed before the conversion, which is the first thing anyone tries.
Why it earned a fix rather than a footnote
An agent exploring the product hit this on the prospect-search endpoints and reported to its user:
The feature works on that account; sibling runs in the same batch got
200. An environment quirkbecame a confident, wrong statement about someone's own entitlements. This CLI is meant to be
driven by agents, and an agent cannot tell a mangled path from a missing endpoint when both produce
the same output.
One cause, not two
It was first reported as two findings — "any path without a query string returns a false 404" plus
"and separately, Git Bash rewrites the path". It is one cause. A
?stops MSYS treating theargument as a path, so adding a dummy query string only appeared to fix it. Verified with
--verbose, which prints the URL actually requested:reply api /v3/whoami(Git Bash)…/C:/Program Files/Git/v3/whoamireply api "/v3/whoami"(Git Bash)…/C:/Program Files/Git/v3/whoamireply api "/v3/whoami?_=1"…/v3/whoami?_=1reply api //v3/whoami…/v3/whoamiMSYS_NO_PATHCONV=1 reply api /v3/whoami…/v3/whoamireply api /v3/whoami(PowerShell)…/v3/whoamiScope is narrower than first reported: Windows + Git Bash/MSYS only.
The fix
The path is validated before anything is spent — before auth, before a request. It must start with
/. Beyond that:instead of described:
C:/Program Files/Git/v3/whoamiyieldsreply api //v3/whoami. Bothseparators are handled, since MSYS emits forward slashes but a path pasted from cmd arrives with
backslashes.
actually tested. Shipping an untested workaround in an error message would be the same class of
defect as the bug itself.
1.A path without a leading slash that is not shell mangling gets plain guidance instead, with no
MSYS noise.
Found on the way: 47 hints that nobody could see
The top-level handler printed only
error.messageand droppedhintentirely. Every hint on aUsageErrororRuntimeErrorwas written and never shown — 47 call sites, including the onethat explains how to switch team, which this project treats as the canonical example of an
actionable error.
Api_errorwas unaffected because it bakes its hint into the message, which iswhy this went unnoticed.
Hints now print for all three, reusing
Api_error's existingHint: …shape so both kinds offailure read the same. Continuation lines keep their own indentation, which is what makes a quoted
command stand out.
--jsonis unchanged — the hint was already into_json().This is a behaviour change beyond the reported bug, and deliberately so: the fix's whole value is
an actionable message, and without it the new error would have printed one line and swallowed the
remedy.
Verification
/vN/segment, the non-mangled slashless case, the exit code, andformat_hintincluding themulti-line shape.
spending a call.
both recommended remedies return
200; a genuine 404 still exits 1.Out of scope
The
401from an expired local CSM key noted in the same investigation — unrelated to the CLI.OAuth through the CLI works.