You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Hackathon] fix(setup): a timeout while verifying the key is reported as "API key rejected", exit 3, instead of REQUEST_TIMEOUT exit 7 #614
setup checks a key with GET /me before saving it. When that request times out on the client side, setup says the key was rejected and exits 3, the auth family. The same stall on any other command gives REQUEST_TIMEOUT and exit 7.
I reproduced it against a local mock that accepts the connection and never answers:
$ TESTSPRITE_API_KEY=sk-user-... testsprite --request-timeout 2 --output json setup --from-env --no-agent
API key rejected by http://127.0.0.1:35585: Request timed out after 2s (client-side). Use --request-timeout <seconds> or TESTSPRITE_REQUEST_TIMEOUT_MS to extend the deadline. [...] profile NOT updated
{
"error": {
"code": "CLI_ERROR",
"message": "API key rejected by http://127.0.0.1:35585: Request timed out after 2s (client-side). [...] did you mean to set TESTSPRITE_API_URL?",
"nextAction": "",
"requestId": "local",
"details": {}
}
}
exit=3
$ testsprite --request-timeout 2 --output json project list # same stalled server
{ "error": { "code": "REQUEST_TIMEOUT", ..., "requestId": "cli_0b3c873d-...", "details": { "timeoutMs": 2000 } } }
exit=7
setup saves nothing in this case. A CI job that runs setup --from-env through a proxy that holds connections open, or against a slow API with a short --request-timeout, fails with "API key rejected" and exit 3, and the natural reaction is to rotate a key that works. The error also loses the typed code, the nextAction, and the cli_… request id the user would send to support.
Details / implementation notes
Root cause. The catch around the ping in runConfigure (src/commands/auth.ts) rethrows InterruptError and any ApiError unchanged and wraps everything else in new CLIError("API key rejected by ...", 3). RequestTimeoutError extends CLIError, not ApiError (src/lib/errors.ts), so the catch wraps it. The comment on that branch only expects "a TypeError from a misconfigured fetchImpl", and the existing test asserts toBeInstanceOf(CLIError) without checking the exit code.
Proposed fix. Rethrow RequestTimeoutError unchanged next to the InterruptError rethrow, or rethrow any CLIError that already carries a typed code, and keep the "profile NOT updated" stderr line. This is the same family as #397 (a preflight timeout reported as exit 1). #470 is the sibling about which deadline this ping uses.
Tests that fail before the fix.
setup --from-env against a fetch that never resolves exits 7 with REQUEST_TIMEOUT, keeps the cli_ request id, and writes no credentials.
A 401 from /me still exits 3 with the typed envelope.
Confirmations
I have searched existing issues and this is not a duplicate.
I have provided my Discord identity above for reward coordination.
Confirmed on v0.15.0, thanks @Andy00L. runConfigure's catch around the /me check (auth.ts:299-320) wraps every non-ApiError in an exit-3 "API key rejected" error, and RequestTimeoutError extends CLIError, not ApiError, so a timeout during setup tells the user their key was rejected and to check TESTSPRITE_API_URL.
Accepted, scope:
Rethrow RequestTimeoutError unchanged (exit 7, REQUEST_TIMEOUT, request id kept), and make the stderr line say the check timed out rather than "rejected". Keep "profile NOT updated".
Tests (each must fail before the fix): setup --from-env against a fetch that never resolves exits 7 with REQUEST_TIMEOUT and writes no credentials; a 401 from /me still exits 3 with the typed envelope.
Thanks for the interest, @Lexiie! @Andy00L opened this issue, so it's reserved for them until 2026-10-24T07:08:47Z (48 hours after it was accepted). If they haven't claimed it by then, comment /assign again.
Activity
fyZhang66 commentedon Oct 22, 2026
Confirmed on v0.15.0, thanks @Andy00L.
runConfigure's catch around the/mecheck (auth.ts:299-320) wraps every non-ApiErrorin an exit-3 "API key rejected" error, andRequestTimeoutErrorextendsCLIError, notApiError, so a timeout duringsetuptells the user their key was rejected and to checkTESTSPRITE_API_URL.Accepted, scope:
RequestTimeoutErrorunchanged (exit 7,REQUEST_TIMEOUT, request id kept), and make the stderr line say the check timed out rather than "rejected". Keep "profile NOT updated".setup --from-envagainst a fetch that never resolves exits 7 withREQUEST_TIMEOUTand writes no credentials; a 401 from/mestill exits 3 with the typed envelope.No new dependency. Comment
/assignto claim it.Lexiie commentedon Oct 22, 2026
/assign
testsprite-hob commentedon Oct 22, 2026
Thanks for the interest, @Lexiie! @Andy00L opened this issue, so it's reserved for them until 2026-10-24T07:08:47Z (48 hours after it was accepted). If they haven't claimed it by then, comment
/assignagain.Andy00L commentedon Oct 22, 2026
/assign
testsprite-hob commentedon Oct 22, 2026
Assigned to @Andy00L. Thanks for taking this on. (0 more slots available.)
Your open assigned issues (3/3):
--target-urlpreflight, so every PR's run is charged against the Vercel login page #620 — [Hackathon] fix(preflight): a Vercel preview behind Deployment Protection passes the--target-urlpreflight, so every PR's run is charged against the Vercel login page