{"record":{"id":"66a4b0e4c2f76b46","repo":"clockworklabs/SpacetimeDB","slug":"failed-to-get-token","errorCode":null,"errorMessage":"Failed to get token: {}","messagePattern":"Failed to get token: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/subcommands/login.rs","lineNumber":277,"sourceCode":"#[derive(Deserialize, Debug)]\nstruct SpacetimeDBTokenData {\n    token: String,\n}\n\nasync fn spacetimedb_login(remote: &Url, web_session_token: &String) -> Result<String, anyhow::Error> {\n    let client = reqwest::Client::new();\n\n    let response: SpacetimeDBTokenResponse = client\n        .post(remote.join(\"api/spacetimedb-token\")?)\n        .header(\"Authorization\", format!(\"Bearer {web_session_token}\"))\n        .send()\n        .await?\n        .error_for_status()?\n        .json()\n        .await?;\n\n    if !response.success {\n        return Err(anyhow::anyhow!(\n            \"Failed to get token: {}\",\n            response.error.unwrap_or(\"Unknown error\".to_string())\n        ));\n    }\n    Ok(response.data.unwrap().token.clone())\n}\n\n#[derive(Debug, Clone, Deserialize)]\nstruct LocalLoginResponse {\n    pub token: String,\n}\n\nasync fn spacetimedb_direct_login(host: &Url) -> Result<String, anyhow::Error> {\n    let client = reqwest::Client::new();\n    let response: LocalLoginResponse = client\n        .post(host.join(\"/v1/identity\")?)\n        .send()\n        .await?","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/cli/src/subcommands/login.rs#L259-L295","documentation":"Final exchange of web login: after browser approval, the CLI posts the web session token to api/spacetimedb-token with a Bearer header to obtain the persistent SpacetimeDB API token. If the endpoint answers success: false, the CLI surfaces the server-provided error string (or 'Unknown error' if the field is absent). HTTP failures surface earlier as reqwest/status errors; this is an application-level rejection.","triggerScenarios":"The spacetimedb-token endpoint returns {success: false, error: ...} — e.g. the web session token expired between approval and exchange, the session was revoked/consumed, or the auth backend rejects the exchange during an incident. The message after 'Failed to get token:' is the server's own error text and is the key diagnostic.","commonSituations":"Slow clock or long delay between approving in the browser and the CLI polling the exchange; clock skew on the client making the session appear expired; re-running login while a previous session token is still in flight; auth backend version skew.","solutions":["Read the server message after 'Failed to get token:' — expiry/invalid-session messages mean: restart `spacetime login` and approve promptly","Check client clock skew (session validation is time-sensitive) and resync NTP","Update the CLI and retry against maincloud to rule out self-hosted auth version mismatches"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"# Rule out clock skew, a common cause of 'session expired' exchange failures:\ntimedatectl status | grep -i 'synchronized' || echo 'resync system clock before login'","typeGuard":null,"tryCatchPattern":"until spacetime login; do echo \"exchange failed — restart flow and approve promptly\"; done","preventionTips":["Approve promptly and let the CLI finish — don't suspend the terminal mid-login","Keep system clocks NTP-synced; session validation is time-sensitive","Read the server-provided message after 'Failed to get token:' — it names the actual rejection reason"],"tags":["cli","login","auth","token-exchange","api-error","network"],"backgroundTag":"auth-token-exchange-failed","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}