Skip to content

fix(auth): stop blaming another sign-in when a busy port blocks browser sign-in - #148

Merged
bman654 merged 1 commit into
mainfrom
fix/oauth-message-and-comment-accuracy
Aug 26, 2026
Merged

bman654 merged 1 commit into
mainfrom
fix/oauth-message-and-comment-accuracy

Conversation

@bman654

@bman654 bman654 commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Summary

When browser sign-in can't start because its ports are already taken, clodex used to tell you to
"close any other OpenAI sign-in (e.g. codex login)" — even though all it had actually observed was
that the ports were busy. Any program holding port 1455 or 1457 causes the same failure, so that
advice could send you hunting for a codex login that was never running. The error now leads with
what was actually observed, offers another OpenAI sign-in as the first thing to check rather than
the cause, and says plainly that any other process holding those ports will do it.

This is the same defect as #142 — an error message asserting a cause it never observed — on a
neighbouring path. It was found while reviewing the fix for #142 in #146.

Changes

src/oauth/openai.ts — the busy-port message. Before / after, as the real CLI prints them with
both ports held:

Ports 1455 and 1457 are in use — close any other OpenAI sign-in (e.g. codex login) and try again.

Ports 1455 and 1457 are in use — browser sign-in needs one free. Check for another OpenAI sign-in
(e.g. `codex login`), or any other process holding them, then try again.

The remedy is now correct whoever holds the port. It renders at 170 columns, down from an
intermediate 218-column draft.

Two comments that made false claims. Neither changes behaviour; both were actively misleading.

  • src/oauth/callback-server.ts opened by describing itself as a "CLI fallback" whose "primary
    path" is a GUI server handling /oauth/callback. There is no GUI server, and no /oauth/callback
    route exists anywhere — this server serves the callback path its caller registers, and since feat(auth): add browser sign-in for workspaces that disable device codes #141
    it is the only thing that receives a browser sign-in callback. At the parent commit, the only
    occurrences of "GUI" and "/oauth/callback" under src/ were inside that comment describing them.
    A reviewer following it would conclude the code they were changing was a secondary path.
  • src/oauth/openai.ts's post-callback state check was labelled "defense in depth", which
    understates it: this flow always passes expectedState, so the callback server answers 400 and
    keeps waiting rather than ever delivering a mismatched state to that branch. The guard is
    deliberately kept
    — removing a redundant check in auth code trades a real safety margin for a
    cosmetic cleanup — but the comment no longer implies it is what protects the flow today.

Tests

tests/oauth-openai.test.ts asserted only a loose regex on the port numbers, so it passed unchanged
against the new wording. It now compares the Error for equality.

That distinction is the substantive test change here. expect(...).rejects.toThrow('<string>') is a
substring match in Vitest, so an assertion written that way lets appended copy through
unasserted. Verified by appending ARBITRARY UNPINNED SUFFIX to the production message: under
toThrow(string) the file stayed green at 22/22; under toThrowError(new Error(...)) it fails.

Mutations run, full-file (not -t isolation):

Mutation Result
Revert the message to the old wording new assertion red
Append arbitrary prose to the message red (was green before this change)
Unmutated 22/22 green

Verification

  • pnpm typecheck && pnpm test && pnpm build green under an isolated CLODEX_HOME
    103 files / 1950 tests. Re-run 5× consecutively for stability.
  • Real CLI, not just assertions: drove clodex providers auth openai --browser with both ports held
    by a bare TCP server and read the bytes on fd 1 (exit 1). Also drove it holding only 1455 and
    confirmed the flow binds 1457 and completes — so "needs one free" is accurate, not a guess.

Notes and limits

  • The one-element case still reads "Ports 52362 are in use", and a busy port followed by an invalid
    one falls through to the generic listener error. Both need the internal ports option; the
    supported CLI always supplies the two fixed ports. Hygiene, not fixed here.
  • callback-server.ts's own timeout message says the browser "closed" without observing that. It is
    the same class again, but fix(oauth): report ignored callbacks in sign-in timeout #146 addressed the reachable case and this PR does not reopen it.

…er sign-in

The busy-port error asserted a cause it never observed: it told the user to "close any other
OpenAI sign-in (e.g. codex login)" on the sole evidence that ports 1455/1457 were occupied. Any
process holding those ports produces the same bind failure, so the advice could send a user
hunting for a codex login that was never running. The message now leads with what was actually
observed, offers that sign-in as the first thing to check rather than the cause, and says any
other process holding the ports will do it.

Same defect class as the timeout message fixed in #146/#142, found while reviewing that change.

Also corrects two comments that made false claims about this code:

- callback-server.ts's header described itself as a "CLI fallback" whose "primary path" is a GUI
  server handling /oauth/callback. No GUI server exists and no /oauth/callback route exists
  anywhere; this server serves the callback path its caller registers and, since #141, is the
  sole receiver for browser sign-in. The only occurrences of "GUI" and "/oauth/callback" in src/
  were inside that comment. The UI server that owned this module's job went with the strip to the
  Claude-to-OpenAI bridge (d01d0eb) — for the since-removed Antigravity flow, not for OpenAI,
  which used device code at the time.
- openai.ts's post-callback state check was labelled "defense in depth", which understates it:
  this flow always passes expectedState, so the server rejects a mismatch with 400 before it can
  reach that branch. The guard is kept as a backstop, but the comment no longer implies it is
  what protects the flow today.

The existing busy-port test asserted only a loose regex on the port numbers, which passed
unchanged against the new wording. It now compares the Error for equality: toThrow(string) is a
substring match in Vitest, so an assertion written that way still lets appended copy through
unasserted — verified by appending prose to the message and watching the test stay green.
@bman654
bman654 merged commit 0b239fa into main Aug 26, 2026
5 checks passed
@bman654
bman654 deleted the fix/oauth-message-and-comment-accuracy branch August 27, 2026 02:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant