Kuberwastaken/claurst · error

Poll: no token

Error message

Poll: no token

What it means

`poll_messages` fetches new Remote Control messages from the server and requires the session token for the Authorization header. If `config.session_token` is `None` at poll time, the call fails with this error instead of issuing an unauthenticated request. Unlike registration, this can occur mid-session if the token was somehow cleared after registration.

Solutions

  1. Ensure the token is present before starting the poll loop — bail early at startup (the start path already validates this).
  2. If the token can rotate, store it in an `Arc<RwLock<Option<String>>>` and refresh rather than setting it to None.
  3. Restart the bridge via the normal start path so the token is resolved from CLAURST_BRIDGE_TOKEN.

Example fix

// before
if bridge.config.session_token.is_none() {
    bridge.run_poll_loop().await?; // Poll: no token
}
// after
if bridge.config.session_token.is_none() {
    anyhow::bail!("Cannot start poll loop without a session token");
}
bridge.run_poll_loop().await?;
Defensive patterns

Strategy: validation

Validate before calling

// Guard the poll loop entry
assert!(bridge.config.session_token.is_some(), "poll requires a session token");

Try / catch

match bridge.poll_messages().await {
    Err(e) if e.to_string().contains("no token") => {
        // stop the loop and surface a config error instead of spinning
        state.set(Disconnected);
        return Err(e);
    }
    other => other,
}

Prevention

When it happens

Trigger: `poll_messages` (invoked from `run_poll_loop`) executed while `self.config.session_token` is `None` — typically a bridge built without a token, or a config mutated after startup.

Common situations: Reusing a bridge instance whose config was rebuilt without the token; tests constructing a bare BridgeConfig; a long-running loop after the token was removed from config.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/7f9e0cb2fdc0eb91. Report an issue: GitHub.

Appendix: source

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

    }

    // -----------------------------------------------------------------------
    // Polling
    // -----------------------------------------------------------------------

    /// Long-poll for incoming messages from the web UI.
    ///
    /// GET `/api/claude_code/sessions/{id}/poll`
    ///
    /// - `200` → JSON array of [`BridgeMessage`]; may be empty.
    /// - `204` → No messages; returns empty vec.
    /// - `401`/`403` → Auth failure; sets state to `Disconnected` and errors.
    async fn poll_messages(&self) -> anyhow::Result<Vec<BridgeMessage>> {
        let token = self
            .config
            .session_token
            .as_deref()
            .ok_or_else(|| anyhow::anyhow!("Poll: no token"))?;

        let url = format!(
            "{}/api/claude_code/sessions/{}/poll",
            self.config.server_url, self.session_id
        );

        let resp = self
            .http
            .get(&url)
            .bearer_auth(token)
            .timeout(std::time::Duration::from_secs(35))
            .send()
            .await
            .context("Bridge poll: HTTP send failed")?;

        let status = resp.status().as_u16();
        match status {
            200 => {

View on GitHub (pinned to b0637c97ec)