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
- Ensure the token is present before starting the poll loop — bail early at startup (the start path already validates this).
- If the token can rotate, store it in an `Arc<RwLock<Option<String>>>` and refresh rather than setting it to None.
- 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
- Validate the token once at startup and pass it through the loop, never re-reading possibly-cleared config.
- Keep the token immutable for the bridge's lifetime (Arc<str>) to prevent accidental clearing.
- Stop the poll loop on auth errors instead of retrying without credentials.
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
- Bridge register: no session token
- Upload: no token
- Remote Control requires a session token. Set…
- No API key found. Options: - Set ANTHROPIC_API_KEY for…
- Login succeeded but could not obtain a usable credential
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)