decolua/9router · error · Error
Port 56121 in use; close the conflicting process and retry
Error message
Port 56121 in use; close the conflicting process and retry
What it means
For the xAI (Grok) flow, the modal starts a server-side callback proxy that must listen on the fixed port 56121. If /api/oauth/xai/start-proxy reports `reason: "port_busy"` (proxy not started) the modal throws this hard error instead of falling back, because the callback redirect_uri is hardwired to http://127.0.0.1:56121/callback.
Source
Thrown at src/shared/components/OAuthModal.js:349
}
}
// xAI: same fixed-port server-side proxy pattern as codex (port 56121)
let xaiProxyActive = false;
let xaiServerSide = false;
if (provider === "xai") {
try {
const proxyUrl = new URL(`/api/oauth/xai/start-proxy`, window.location.origin);
proxyUrl.searchParams.set("app_port", appPort);
proxyUrl.searchParams.set("state", data.state);
proxyUrl.searchParams.set("code_verifier", data.codeVerifier);
proxyUrl.searchParams.set("redirect_uri", redirectUri);
const proxyRes = await fetch(proxyUrl.toString());
const proxyData = await proxyRes.json();
xaiProxyActive = proxyData.success;
xaiServerSide = !!proxyData.serverSide;
if (!xaiProxyActive && proxyData.reason === "port_busy") {
throw new Error("Port 56121 in use; close the conflicting process and retry");
}
} catch (e) {
if (e?.message) throw e;
xaiProxyActive = false;
}
}
setAuthData({ ...data, redirectUri, codexServerSide, xaiServerSide });
// Guard: device_code providers return authUrl:null from /authorize. Never window.open(null)
// (browsers coerce it to the relative path ".../null").
if (!data.authUrl) {
if (data.flowType === "device_code") {
throw new Error(
`Provider ${provider} uses device-code login but is not wired in the OAuth modal device-code list`
);
}
throw new Error("No authorization URL returned from OAuth provider");View on GitHub (pinned to 90b52e06ff)
Solutions
- Find the process holding port 56121 (lsof -i :56121 or netstat -ano) and kill it
- Retry the xAI OAuth flow after the port is free
- Restart the 9Router app to clean up stale proxy processes
- If a persistent conflict exists, change the conflicting service's port
Example fix
// shell check before retrying // before: retry while port still busy lsof -ti :56121 | xargs kill // after: free the port, then retry the flow
Defensive patterns
Strategy: fallback
Validate before calling
// pre-check before starting the flow (node)
const net = require('net');
const s = net.connect(56121, '127.0.0.1');
s.once('connect', () => { console.error('port 56121 busy'); s.destroy(); });
s.once('error', () => console.log('port 56121 free')); Type guard
null
Try / catch
try {
await startXaiFlow();
} catch (e) {
if (e.message.includes("Port 56121")) {
// instruct: lsof -ti :56121 | xargs kill, then retry
}
} Prevention
- Free port 56121 before starting xAI OAuth (lsof -i :56121)
- Avoid running multiple dashboard instances concurrently
- Restart the app after a crashed session to clean stale proxies
When it happens
Trigger: Starting xAI OAuth while another process is bound to TCP port 56121 — a leftover previous 9router/xAI proxy instance, another tool using that port, or a stale zombie process.
Common situations: A crashed previous session left the proxy running; a second dashboard instance is open; another dev service happens to occupy 56121.
Related errors
- xAI OAuth session not found; restart the login flow and past
- Missing xAI authorization code
- xai discovery ${field} is empty
- xai discovery ${field} is invalid: ${err.message}
- `xai discovery ${field} must use https: ${value}`
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/a87391a15ea63231.
Report an issue: GitHub.