coleam00/Archon · error · OAuthCallbackPortBusyError
A previous '${provider}' login attempt is still holding the
Error message
A previous '${provider}' login attempt is still holding the OAuth callback port. Wait a few seconds and retry; if it persists, restart the Archon server. What it means
OAuthCallbackPortBusyError thrown by startOAuth (oauth-bridge.ts:383). Before starting a new login the bridge cancels prior in-flight/superseded sessions; if the freshly started login fails immediately with a port-in-use error on the vendor's fixed callback port (anthropic binds 53692), the failure is classified via PORT_BUSY_RE and surfaced as this retryable error. It means a previous login attempt's callback server is still holding the port.
Source
Thrown at packages/core/src/credentials/oauth-bridge.ts:383
session.firstSignal.resolve(true);
getLog().warn(
{ err: sanitizeError(err as Error), userId, provider },
'oauth_bridge.login_failed'
);
});
// Wait for the first callback so the URL / user-code is available to return.
await Promise.race([session.firstSignal.promise, sleep(START_FIRST_SIGNAL_MS)]);
// An early login() failure → throw (route returns 500, CLI prints the message)
// rather than returning a misleading { mode:'manual', url:undefined } (I1).
if (session.status === 'error') {
sessions.delete(sessionId);
if (session.portBusy) {
// Retryable: the cancel above releases the port as soon as the previous
// login unwinds (microtasks for pi flows), so "retry shortly" is honest
// advice — and a restart always clears it (#1963).
throw new OAuthCallbackPortBusyError(
`A previous '${provider}' login attempt is still holding the OAuth callback port. ` +
'Wait a few seconds and retry; if it persists, restart the Archon server.'
);
}
throw new Error(session.detail ?? 'Subscription login failed to start.');
}
// Superseded (or cancelled) while still waiting for the first signal — the
// session is already gone from the map, so a 200 here would hand back a
// url-less session the first poll immediately reports as "not found".
// Throw the honest answer instead (S4).
if (!sessions.has(sessionId)) {
throw new Error('Login attempt was superseded by a newer one. Retry to start a fresh login.');
}
return {
sessionId,
mode: externalMode(session),View on GitHub (pinned to 0773b97458)
Solutions
- Wait a few seconds and retry — the superseded login releases the port as soon as it unwinds (microtasks for pi flows).
- If it persists, restart the Archon server, which always frees the bound port.
- Catch OAuthCallbackPortBusyError and implement bounded retry with backoff in your client.
- Avoid firing a new login while one for the same vendor is still in flight; cancel it first (cancelOAuth) and let it settle.
Example fix
// before
await startOAuth(userId, 'anthropic');
// after
try {
await startOAuth(userId, 'anthropic');
} catch (e) {
if (e instanceof OAuthCallbackPortBusyError) {
await sleep(3000);
await startOAuth(userId, 'anthropic');
} else throw e;
} Defensive patterns
Strategy: retry
Validate before calling
// Pre-check: ensure no login for a callback-server vendor is already in flight
if (hasActiveOAuthSession(userId, provider)) {
await cancelOAuth(userId, provider); // or wait for it to settle
} Type guard
function isOAuthCallbackPortBusyError(e: unknown): e is OAuthCallbackPortBusyError {
return e instanceof OAuthCallbackPortBusyError;
} Try / catch
try {
await startOAuth(userId, 'anthropic');
} catch (e) {
if (e instanceof OAuthCallbackPortBusyError) {
await sleep(3000);
await startOAuth(userId, 'anthropic'); // bounded retry; restart server if it persists
} else {
throw e;
}
} Prevention
- Never start a second login for the same vendor while one is in flight.
- Cancel in-flight logins explicitly and await settlement before restarting.
- Restart the Archon server after crashes that may have leaked the callback port.
- Surface this error class distinctly in UI so users see it as retryable.
When it happens
Trigger: Calling startOAuth for a callback-server vendor (anthropic) while a previous login attempt's local callback server still holds the fixed port — the new pi login() rejects with EADDRINUSE, session.portBusy is set, and startOAuth throws this error instead of an opaque failure (#1963).
Common situations: An abandoned browser login from minutes ago still unwinding; a crashed/hung previous login that never released port 53692; rapid restart attempts of the same login; long-lived server process that accumulated a leaked callback server.
Related errors
- err.message (OAuthCallbackPortBusyError)
- Provider '${provider}' does not support subscription login.
- Vendor '${vendor}' (Pi backend) has no env-based OAuth deliv
- OAuth state mismatch.
- Missing authorization code.
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/1b28894a2442d11c.
Report an issue: GitHub.