{"record":{"id":"ce62b0f0b78b0057","repo":"tinyhumansai/openhuman","slug":"login-token-is-required","errorCode":null,"errorMessage":"login token is required","messagePattern":"login token is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/api/rest.rs","lineNumber":516,"sourceCode":"        let state = value\n            .get(\"state\")\n            .and_then(Value::as_str)\n            .filter(|state| !state.is_empty())\n            .map(str::to_owned)\n            .context(\"missing state\")?;\n        Ok(ConnectResponse { oauth_url, state })\n    }\n\n    /// Fetches the current authenticated user profile using the provided JWT.\n    pub async fn fetch_current_user(&self, bearer_jwt: &str) -> Result<Value> {\n        self.authed_json(bearer_jwt, Method::GET, \"auth/me\", None)\n            .await\n    }\n\n    /// Exchanges a one-time login token (e.g. from Telegram) for a long-lived JWT.\n    pub async fn consume_login_token(&self, login_token: &str) -> Result<String> {\n        let token = login_token.trim();\n        anyhow::ensure!(!token.is_empty(), \"login token is required\");\n\n        // Backend serves `POST /auth/login-token/consume` with the token in a JSON\n        // body `{ token, audience? }` and returns `{ success, data: { jwt } }`\n        // (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)","sourceCodeStart":498,"sourceCodeEnd":534,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/api/rest.rs#L498-L534","documentation":"Thrown by BackendOAuthClient::consume_login_token when the one-time login token is empty after trimming. These tokens (e.g. from a Telegram bot deep link) are exchanged at POST /auth/login-token/consume; an empty token means the caller never extracted it from its carrier (URL param, bot payload) before calling. Pure input validation, no request is made.","triggerScenarios":"Calling consume_login_token(\"\") or consume_login_token(\"   \") — e.g. a deep-link handler parsed the URL but the token query param was absent, or a bot start payload regex matched an empty capture group.","commonSituations":"Deep-link format changed so the token param moved or was dropped, the bot payload regex no longer matches, or a test fixture left the token field blank.","solutions":["Extract the token from its carrier first and confirm it is non-empty before calling","Log the raw incoming deep link / payload (redacted) when the token is missing to see which parse step lost it","If the token is genuinely absent, re-issue the login link instead of calling consume"],"exampleFix":"// before\nlet jwt = client.consume_login_token(&token).await?; // token = \"\"\n\n// after\nlet token = params.get(\"token\").map(str::trim).unwrap_or(\"\");\nanyhow::ensure!(!token.is_empty(), \"login link is missing its token; request a new one\");\nlet jwt = client.consume_login_token(token).await?;","handlingStrategy":"validation","validationCode":"let token = login_token.trim();\nanyhow::ensure!(!token.is_empty(), \"login token missing from deep link; request a new one\");\nlet jwt = client.consume_login_token(token).await?;","typeGuard":"fn extract_login_token(url: &str) -> Option<String> {\n    url::Url::parse(url).ok()?\n        .query_pairs()\n        .find(|(k, _)| k == \"token\")\n        .map(|(_, v)| v.trim().to_string())\n        .filter(|t| !t.is_empty())\n}","tryCatchPattern":null,"preventionTips":["Parse the deep link and assert the token param exists before calling consume","Treat a missing token as 're-issue login link', not as an error to retry"],"tags":["rust","validation","auth","login-token"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}