tinyhumansai/openhuman · error
login token is required
Error message
login token is required
What it means
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.
Source
Thrown at src/api/rest.rs:516
let state = value
.get("state")
.and_then(Value::as_str)
.filter(|state| !state.is_empty())
.map(str::to_owned)
.context("missing state")?;
Ok(ConnectResponse { oauth_url, state })
}
/// Fetches the current authenticated user profile using the provided JWT.
pub async fn fetch_current_user(&self, bearer_jwt: &str) -> Result<Value> {
self.authed_json(bearer_jwt, Method::GET, "auth/me", None)
.await
}
/// Exchanges a one-time login token (e.g. from Telegram) for a long-lived JWT.
pub async fn consume_login_token(&self, login_token: &str) -> Result<String> {
let token = login_token.trim();
anyhow::ensure!(!token.is_empty(), "login token is required");
// Backend serves `POST /auth/login-token/consume` with the token in a JSON
// body `{ token, audience? }` and returns `{ success, data: { jwt } }`
// (see backend `routes/auth.ts`). The legacy
// `telegram/login-tokens/{token}/consume` path-param route was removed, so
// the old call 404'd and Telegram/OAuth-token login could never complete
// (WIRING_GAPS_AUDIT C1/C2).
let response = self
.sdk
.auth()
.consume_login_token(&tinyhumans_sdk::api::types::LoginTokenRequest {
token: token.to_string(),
})
.await
.context("consume login token through TinyHumans SDK")?;
let jwt = response
.get("jwt")
.and_then(Value::as_str)View on GitHub (pinned to a221052e0d)
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
Example fix
// before
let jwt = client.consume_login_token(&token).await?; // token = ""
// after
let token = params.get("token").map(str::trim).unwrap_or("");
anyhow::ensure!(!token.is_empty(), "login link is missing its token; request a new one");
let jwt = client.consume_login_token(token).await?; Defensive patterns
Strategy: validation
Validate before calling
let token = login_token.trim(); anyhow::ensure!(!token.is_empty(), "login token missing from deep link; request a new one"); let jwt = client.consume_login_token(token).await?;
Type guard
fn extract_login_token(url: &str) -> Option<String> {
url::Url::parse(url).ok()?
.query_pairs()
.find(|(k, _)| k == "token")
.map(|(_, v)| v.trim().to_string())
.filter(|t| !t.is_empty())
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- API base URL must be an absolute http(s) URL with host
- provider is required
- consume login token response missing jwt
- channel is required
- integrationId must be a 24-char hex id
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/ce62b0f0b78b0057.
Report an issue: GitHub.