decolua/9router · error

Qoder device token poll: invalid JSON response (${err.messag

Error message

Qoder device token poll: invalid JSON response (${err.message})

What it means

The poll endpoint returned HTTP 200, but the response body could not be parsed as JSON. The service reads the body as text first and JSON.parse's it; on parse failure it throws with the underlying parser message embedded. This signals an upstream contract violation or a non-JSON payload (e.g. an HTML error page served with status 200).

Source

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

      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",
      accessToken: body.token,
      refreshToken: body.refresh_token || "",
      userId: body.user_id || "",
      expireTime: expireMs,
      rawResponse: body,
    };
  }

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Log the raw body alongside the error to see what was actually returned (the err.message only shows the parse offset).
  2. Check for transparent proxies / TLS interception that inject HTML into responses.
  3. Retry the poll once — intermittent gateway 200-with-empty-body can self-heal.
  4. If persistent, verify the Qoder endpoint URL/contract hasn't changed and update QODER_DEVICE_TOKEN_URL handling.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const result = await qoder.pollDeviceToken({ nonce, codeVerifier });
} catch (e) {
  if (e.message.startsWith('Qoder device token poll: invalid JSON response')) {
    log.warn('non-JSON 200 from Qoder, likely proxy/interstitial', { err: e.message });
    if (attempt < maxAttempts) return retryLater();
  }
  throw e;
}

Prevention

When it happens

Trigger: Qoder returns an HTML error/captive-portal page with status 200, an empty body, or a CDN interstitial; proxy middleware rewrites the response body.

Common situations: Corporate proxies or VPNs intercepting HTTPS traffic; Qoder changing the endpoint to return plain text; intermittent gateway returning empty 200s during incidents.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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