decolua/9router · error

${message}

Error message

${message}

What it means

Thrown when Qoder's device-token poll endpoint answers with a non-2xx status. The message is 'Qoder device token poll failed: HTTP <status>', upgraded to include the upstream JSON 'message' field when the body parses as JSON. Per the service contract, upstream returns 202/404 while the user is still authorizing; any other non-ok status reaching this throw is a terminal failure of the device flow.

Source

Thrown at src/lib/oauth/services/qoder.js:125

        "User-Agent": "Go-http-client/2.0",
      },
    });

    // Pending — server has registered the device code but the user hasn't
    // finished the browser flow yet. Both 202 and 404 mean "keep polling".
    if (response.status === 202 || response.status === 404) {
      return { status: "pending" };
    }

    const text = await response.text();

    if (!response.ok) {
      let message = `Qoder device token poll failed: HTTP ${response.status}`;
      try {
        const body = JSON.parse(text);
        if (body.message) message = `Qoder device token poll failed: ${body.message}`;
      } catch {}
      throw new Error(message);
    }

    let body;
    try {
      body = JSON.parse(text);
    } catch (err) {
      throw new Error(`Qoder device token poll: invalid JSON response (${err.message})`);
    }

    // Defensive: 200 + empty token means the upstream changed shape.
    if (!body.token) {
      throw new Error("Qoder device token poll returned 200 but no token");
    }

    const expireMs = QoderService.parseExpiry(body.expires_at, body.expires_in);

    return {
      status: "ok",

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Read the included upstream message in the error text to identify the exact cause (e.g. expired nonce vs unauthorized).
  2. Treat 202/404 as 'keep waiting' in your polling loop and only surface other statuses as fatal.
  3. Restart the device flow (new nonce + code verifier) if the poll indicates expiry or invalid state.
  4. Check Qoder service status / network connectivity if the status is 5xx.

Example fix

// before
const t = await qoder.pollDeviceToken({ nonce, codeVerifier }); // throws on 404 while waiting
// after
try {
  const t = await qoder.pollDeviceToken({ nonce, codeVerifier });
} catch (e) {
  if (/HTTP (202|404)/.test(e.message)) return continuePolling();
  throw e;
}
Defensive patterns

Strategy: retry

Try / catch

try {
  const result = await qoder.pollDeviceToken({ nonce, codeVerifier });
} catch (e) {
  if (/HTTP 202|HTTP 404/.test(e.message)) { await sleep(pollInterval); continue; } // still waiting
  if (/HTTP 5\d\d/.test(e.message)) { await sleep(backoff); continue; }             // transient upstream
  throw e; // 400/401/403: state is bad, restart device flow
}

Prevention

When it happens

Trigger: GET to QODER_DEVICE_TOKEN_URL returns 400/401/403/500 etc. — e.g. an expired or invalid nonce, wrong verifier (PKCE mismatch), or Qoder outage. Note 202/404 while waiting are expected polling states, not this error, if handled by the caller's retry loop.

Common situations: Polling after the device code expired; reusing a nonce from a previous flow; Qoder API version change altering the endpoint contract; corporate proxy returning 502 with an HTML body (which keeps the generic HTTP status message).

Related errors


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