Kuberwastaken/claurst · error · anyhow::Error

Bridge poll: auth error

Error message

Bridge poll: auth error ({})

What it means

After a successful token-exchange HTTP response, `exchange_code` deserializes the body into its internal `TokenResponse` struct (access_token, refresh_token, expires_in, scope). If the body is not valid JSON or does not match the expected shape, the serde/reqwest error is wrapped as "exchange_code: bad JSON: {}". The token endpoint returned 2xx but not the JSON the library expects.

Solutions

  1. Capture and inspect the raw response body (curl the token endpoint with the same form payload) to see what was actually returned.
  2. Verify the token_endpoint URL is correct and returns `application/json` for the authorization-code grant.
  3. If behind a proxy/captive portal, fix the network path so the real provider response arrives.
  4. If the provider uses nonstandard types, file/track support for that provider's wire format rather than re-running the flow.
Defensive patterns

Strategy: try-catch

Try / catch

// on "exchange_code: bad JSON", log the raw body to diagnose
Err(e) if e.to_string().contains("bad JSON") => {
    eprintln!("Token endpoint returned non-JSON; verify token_endpoint and network path: {}", e);
    Err(e)
}

Prevention

When it happens

Trigger: `resp.json::<TokenResponse>()` fails — the endpoint returned HTML (e.g. an error page or login page behind a 200), an empty body, or JSON whose types differ (e.g. expires_in as string instead of u64).

Common situations: A reverse proxy or captive portal returning an HTML 200 page; the provider returning `application/x-www-form-urlencoded` instead of JSON for token responses; nonstandard field types (string "3600" for expires_in); token_endpoint pointing at the wrong URL.

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 Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/36a7995094b4b8ae. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/bridge/src/lib.rs:580

            .send()
            .await
            .context("Bridge poll: HTTP send failed")?;

        let status = resp.status().as_u16();
        match status {
            200 => {
                let text = resp.text().await.context("Bridge poll: reading body")?;
                if text.trim().is_empty() || text.trim() == "[]" {
                    return Ok(vec![]);
                }
                let msgs: Vec<BridgeMessage> =
                    serde_json::from_str(&text).context("Bridge poll: JSON parse")?;
                Ok(msgs)
            }
            204 => Ok(vec![]),
            401 | 403 => {
                self.set_state(BridgeState::Error(format!("Auth error: {status}")));
                anyhow::bail!("Bridge poll: auth error ({})", status)
            }
            _ => {
                anyhow::bail!("Bridge poll: server returned {}", status)
            }
        }
    }

    // -----------------------------------------------------------------------
    // Event upload
    // -----------------------------------------------------------------------

    /// Batch-upload outgoing events to the web UI.
    ///
    /// POST `/api/claude_code/sessions/{id}/events`
    async fn upload_events(&self, events: Vec<BridgeEvent>) -> anyhow::Result<()> {
        if events.is_empty() {
            return Ok(());
        }

View on GitHub (pinned to b0637c97ec)