{"record":{"id":"11cfeed7101fc750","repo":"risingwavelabs/risingwave","slug":"vault-app-role-login-failed","errorCode":null,"errorMessage":"Vault app role login failed: {} - {}","messagePattern":"Vault app role login failed: (.+?) - (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/common/secret/src/vault_client.rs","lineNumber":323,"sourceCode":"                let login_request = VaultAppRoleLoginRequest {\n                    role_id: auth_role_id.clone(),\n                    secret_id: auth_secret_id.clone(),\n                };\n\n                let response = self\n                    .client\n                    .post(login_url.as_str())\n                    .json(&login_request)\n                    .send()\n                    .await\n                    .context(\"Failed to send app role login request\")?;\n\n                if !response.status().is_success() {\n                    // If authentication fails and we have a cached token, invalidate it\n                    if !force_refresh {\n                        GLOBAL_VAULT_TOKEN_CACHE.invalidate(&cache_key).await;\n                    }\n                    return Err(anyhow::anyhow!(\n                        \"Vault app role login failed: {} - {}\",\n                        response.status(),\n                        response.text().await.unwrap_or_default()\n                    ));\n                }\n\n                let auth_response: VaultAuthResponse = response\n                    .json()\n                    .await\n                    .context(\"Failed to parse Vault auth response\")?;\n\n                let token = auth_response.auth.client_token;\n                let lease_duration = auth_response.auth.lease_duration;\n\n                // Cache the token with per-entry expiration based on lease duration (90% of lease duration)\n                let expires_at = Instant::now() + Duration::from_secs((lease_duration * 9) / 10);\n                let cached_token = CachedToken {\n                    token: token.clone(),","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/common/secret/src/vault_client.rs#L305-L341","documentation":"The AppRole login request to Vault (POST /v1/auth/approle/login) returned a non-success HTTP status, so no token could be obtained. When this happens with a previously cached token, that cached token is invalidated in GLOBAL_VAULT_TOKEN_CACHE to force a clean login next time.","triggerScenarios":"get_token_internal sends role_id/secret_id to the AppRole login endpoint and receives e.g. 400 (invalid credentials), 403, or 500; triggered on first use or after a forced token refresh from get_secret.","commonSituations":"Revoked or expired secret_id; wrong role_id; AppRole auth method not enabled on the mount; Vault policy/ACL changes; clock-skew or replayed secret_id with limited uses.","solutions":["Check the status/body in the message: 400 usually means invalid role_id or secret_id","Regenerate the secret_id ('vault write auth/approle/role/<role>/secret-id') and update the secret definition","Verify the AppRole auth method is enabled and the role exists ('vault read auth/approle/role/<role>')","Confirm network reachability and that the login endpoint URL/mount is correct"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-validate AppRole login outside the data path\nlet resp = reqwest::Client::new()\n    .post(format!(\"{vault_addr}/v1/auth/approle/login\"))\n    .json(&serde_json::json!({\"role_id\": role_id, \"secret_id\": secret_id}))\n    .send().await?;\nif !resp.status().is_success() { return Err(\"invalid role_id/secret_id\".into()); }","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"app role login failed\") => {\n        rotate_secret_id_and_retry().await // credentials are likely expired/revoked\n    }\n    other => other,\n}","preventionTips":["Monitor secret_id TTL/uses and rotate before expiry","Verify AppRole role_id and secret_id pairs after any Vault policy change","Ensure the approle auth mount is enabled at the configured path"],"tags":["vault","authentication","approle"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}