mihomo-party-org/clash-party · warning · Error

Login timed out

Error message

Login timed out

What it means

browserLogin starts a local HTTP server and opens a browser for an OAuth authorization-code flow; a setTimeout rejects the promise with 'Login timed out' when the callback with the authorization code does not arrive within timeoutMs. This aborts the pending login so the app does not wait forever on a server that may never receive a redirect.

Source

Thrown at src/main/resolve/plugin/oauth.ts:51

  const open = opts.open ?? ((u: string): Promise<void> => shell.openExternal(u))
  const timeoutMs = opts.timeoutMs ?? CALLBACK_TIMEOUT_MS

  const p = new Promise<OAuthResult>((resolve, reject) => {
    const server = http.createServer()
    let settled = false
    const finish = (fn: () => void): void => {
      if (settled) return
      settled = true
      clearTimeout(timer)
      server.close()
      fn()
    }
    const timer = setTimeout(() => finish(() => reject(new Error('Login timed out'))), timeoutMs)

    server.on('request', (req, res) => {
      const reqUrl = new URL(req.url ?? '/', 'http://127.0.0.1')
      if (reqUrl.pathname !== '/callback') {
        res.writeHead(404)
        res.end()
        return
      }
      const code = reqUrl.searchParams.get('code')
      const retState = reqUrl.searchParams.get('state')
      res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' })
      if (!code || retState !== state) {
        res.end('<html><body>Login failed. You may close this window.</body></html>')
        finish(() => reject(new Error('Invalid OAuth callback (state mismatch or missing code)')))
        return
      }
      res.end('<html><body>Login complete. You may close this window.</body></html>')
      const addr = server.address()
      const port = typeof addr === 'object' && addr ? addr.port : 0
      finish(() => resolve({ code, verifier, redirectUri: `http://127.0.0.1:${port}/callback` }))
    })
    server.on('error', (e) => finish(() => reject(e)))
    server.listen(0, '127.0.0.1', () => {

View on GitHub (pinned to 911e090537)

Solutions

  1. Retry the login (call the login command again) and complete the flow promptly in the opened browser window.
  2. Ensure a default browser is available/openable in the environment, or copy the auth URL and open it manually on another machine — make sure the redirect goes to the same host running the app.
  3. Increase the timeout value if flows routinely need longer.
  4. Check firewall/proxy rules so 127.0.0.1 callback requests are not blocked.

Example fix

// before
await oauth() // user misses the window, rejects after timeoutMs
// after
try {
  await oauth()
} catch (e) {
  if (e.message === 'Login timed out') {
    notifyUser('Login window expired — click Login to try again')
    await oauth()
  } else throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before starting OAuth, check the environment can complete the flow:
if (!process.defaultApp && (process.platform === 'linux' && !process.env.DISPLAY)) {
  console.warn('No GUI available — open the printed auth URL manually on a machine that can reach this host')
}

Try / catch

try {
  await browserLogin()
} catch (e) {
  if (e instanceof Error && e.message === 'Login timed out') {
    // prompt user to retry; optionally increase timeoutMs and re-invoke
    await retryWithBackoff(() => browserLogin(), { retries: 2 })
  } else {
    throw e
  }
}

Prevention

When it happens

Trigger: The user closes the browser window/tab before completing login; the browser never opens (headless/SSH environment, no default browser configured); the OAuth provider page errors or the user abandons the flow; network blocking redirects back to http://127.0.0.1:<port>/callback within the timeout.

Common situations: Running the app on a remote machine without a GUI; corporate proxy/firewall blocking the loopback callback; user multi-tasking and letting the login page sit until it expires; very short timeoutMs configuration.

Understand the failure class

Related errors


AI-assisted analysis of mihomo-party-org/clash-party@911e090537 (2026-08-30). Data as JSON: /api/errors/6b2264a2352bcdc9. Report an issue: GitHub.