clockworklabs/SpacetimeDB · error · anyhow::Error

Failed to get token

Error message

Failed to get token: {}

What it means

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.

Solutions

  1. Read the server message after 'Failed to get token:' — expiry/invalid-session messages mean: restart `spacetime login` and approve promptly
  2. Check client clock skew (session validation is time-sensitive) and resync NTP
  3. Update the CLI and retry against maincloud to rule out self-hosted auth version mismatches
Defensive patterns

Strategy: retry

Validate before calling

# Rule out clock skew, a common cause of 'session expired' exchange failures:
timedatectl status | grep -i 'synchronized' || echo 'resync system clock before login'

Try / catch

until spacetime login; do echo "exchange failed — restart flow and approve promptly"; done

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/66a4b0e4c2f76b46. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/subcommands/login.rs:277

#[derive(Deserialize, Debug)]
struct SpacetimeDBTokenData {
    token: String,
}

async fn spacetimedb_login(remote: &Url, web_session_token: &String) -> Result<String, anyhow::Error> {
    let client = reqwest::Client::new();

    let response: SpacetimeDBTokenResponse = client
        .post(remote.join("api/spacetimedb-token")?)
        .header("Authorization", format!("Bearer {web_session_token}"))
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

    if !response.success {
        return Err(anyhow::anyhow!(
            "Failed to get token: {}",
            response.error.unwrap_or("Unknown error".to_string())
        ));
    }
    Ok(response.data.unwrap().token.clone())
}

#[derive(Debug, Clone, Deserialize)]
struct LocalLoginResponse {
    pub token: String,
}

async fn spacetimedb_direct_login(host: &Url) -> Result<String, anyhow::Error> {
    let client = reqwest::Client::new();
    let response: LocalLoginResponse = client
        .post(host.join("/v1/identity")?)
        .send()
        .await?

View on GitHub (pinned to 6dee26c6ef)