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

  1. Find the process holding port 56121 (lsof -i :56121 or netstat -ano) and kill it
  2. Retry the xAI OAuth flow after the port is free
  3. Restart the 9Router app to clean up stale proxy processes
  4. 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

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


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/a87391a15ea63231. Report an issue: GitHub.