{"record":{"id":"2149292042e4fbe9","repo":"tinyhumansai/openhuman","slug":"consume-login-token-response-missing-jwt","errorCode":null,"errorMessage":"consume login token response missing jwt","messagePattern":"consume login token response missing jwt","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/api/rest.rs","lineNumber":538,"sourceCode":"        // (see backend `routes/auth.ts`). The legacy\n        // `telegram/login-tokens/{token}/consume` path-param route was removed, so\n        // the old call 404'd and Telegram/OAuth-token login could never complete\n        // (WIRING_GAPS_AUDIT C1/C2).\n        let response = self\n            .sdk\n            .auth()\n            .consume_login_token(&tinyhumans_sdk::api::types::LoginTokenRequest {\n                token: token.to_string(),\n            })\n            .await\n            .context(\"consume login token through TinyHumans SDK\")?;\n        let jwt = response\n            .get(\"jwt\")\n            .and_then(Value::as_str)\n            .unwrap_or_default()\n            .trim()\n            .to_string();\n        anyhow::ensure!(!jwt.is_empty(), \"consume login token response missing jwt\");\n        Ok(jwt)\n    }\n\n    /// Validates that the provided session token is still active and accepted.\n    pub async fn validate_session_token(&self, bearer_jwt: &str) -> Result<()> {\n        let _ = self.fetch_current_user(bearer_jwt).await?;\n        Ok(())\n    }\n\n    /// Creates a short-lived link token for connecting a specific communication channel.\n    pub async fn create_channel_link_token(\n        &self,\n        channel: &str,\n        bearer_jwt: &str,\n    ) -> Result<Value> {\n        let channel = channel.trim().trim_matches('/');\n        anyhow::ensure!(!channel.is_empty(), \"channel is required\");\n        let encoded_channel = urlencoding::encode(channel);","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/api/rest.rs#L520-L556","documentation":"Thrown by consume_login_token after a successful SDK call when the response JSON has no non-empty \"jwt\" string field. The request itself completed (context \"consume login token through TinyHumans SDK\" would fire earlier on transport/route failure), so this is a response-shape mismatch: the backend answered 2xx with an envelope that lacks data.jwt. Typical causes are a backend/SDK version skew in the envelope, or an error body returned with a success status.","triggerScenarios":"POST /auth/login-token/consume returns {\"success\":true,\"data\":{}} or an error envelope without jwt — after a backend deploy changed the response shape, or when the token was already consumed and the backend signals that in-band.","commonSituations":"Backend and core versions out of sync (the route moved from the legacy telegram path-param form to the JSON body form), a stale login token being reused, or a proxy/inspection layer rewriting the body.","solutions":["Verify the backend actually serves POST /auth/login-token/consume with the {token} body and returns {success, data:{jwt}} — the legacy path-param route was removed and old callers 404","Log the full (redacted) response body to see which envelope arrived","If the token may be stale or already consumed, restart the login flow to mint a fresh token instead of retrying consume","Align core and SDK versions so the envelope contract matches"],"exampleFix":"// before\nlet jwt = client.consume_login_token(&token).await?;\n\n// after\nlet jwt = match client.consume_login_token(&token).await {\n    Ok(jwt) => jwt,\n    Err(err) if err.to_string().contains(\"missing jwt\") => {\n        anyhow::bail!(\"login token unusable (consumed or backend mismatch); restart login flow\");\n    }\n    Err(err) => return Err(err),\n};","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let jwt = match client.consume_login_token(&token).await {\n    Ok(jwt) => jwt,\n    Err(err) if err.to_string().contains(\"missing jwt\") => {\n        // 2xx but no data.jwt: stale/consumed token or backend envelope skew.\n        // Restart the login flow instead of retrying consume.\n        return Err(err.context(\"login token unusable; restart login flow\"));\n    }\n    Err(err) => return Err(err),\n};","preventionTips":["Pin core, SDK, and backend versions together so the {success, data:{jwt}} envelope contract holds","Log the redacted response body when consume fails to distinguish envelope skew from transport failure","Never reuse a one-time login token; request a fresh link on any consume failure"],"tags":["rust","auth","response-shape","backend-api","version-skew"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}