clockworklabs/SpacetimeDB · error · anyhow::Error

Session token is missing in response.

Error message

Session token is missing in response.

What it means

The final step of web-login session handling: data.approved is true, meaning the user approved in the browser, but data.session_token is None. Since an approved session without a token cannot be exchanged for a SpacetimeDB token, the CLI aborts with 'Session token is missing in response.' rather than proceeding with a null credential.

Solutions

  1. Restart `spacetime login` — if the auth backend was mid-incident, a fresh flow usually completes
  2. Update the CLI to the latest version to match the current auth API contract
  3. If a security product/proxy strips token fields, allowlist the SpacetimeDB auth host and retry
Defensive patterns

Strategy: retry

Try / catch

for i in 1 2; do spacetime login && break; echo "token exchange incomplete — retry $i"; sleep 3; done

Prevention

When it happens

Trigger: Auth API returns success:true, data:{approved:true} but omits the sessionToken field (serde rename). Server-side bug or response-mangling middlebox; in the standard flow the token is always issued on approval, so this is a contract violation by the endpoint.

Common situations: Auth service version skew where approval succeeds but token issuance fails silently server-side; proxies or response filters removing fields containing the word 'token'; partially failing auth backends during incidents.

Related errors


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

Appendix: source

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

impl WebLoginSessionResponse {
    fn approved(self) -> anyhow::Result<Option<WebLoginSessionResponseApproved>> {
        if !self.success {
            return Err(anyhow::anyhow!(self
                .error
                .clone()
                .unwrap_or("Unknown error".to_string())));
        }

        let data = self.data.ok_or(anyhow::anyhow!("Response data is missing."))?;
        if !data.approved {
            // Approved is false, no session token expected
            return Ok(None);
        }

        let session_token = data
            .session_token
            .ok_or(anyhow::anyhow!("Session token is missing in response.".to_string()))?;
        Ok(Some(WebLoginSessionResponseApproved {
            session_token: session_token.clone(),
        }))
    }
}

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

    let response: WebLoginTokenResponse = client
        .post(remote.join("/api/auth/cli/login/request-token")?)
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

    if !response.success {

View on GitHub (pinned to 6dee26c6ef)