risingwavelabs/risingwave · error
Vault app role login failed: {} - {}
Error message
Vault app role login failed: {} - {} What it means
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.
Source
Thrown at src/common/secret/src/vault_client.rs:323
let login_request = VaultAppRoleLoginRequest {
role_id: auth_role_id.clone(),
secret_id: auth_secret_id.clone(),
};
let response = self
.client
.post(login_url.as_str())
.json(&login_request)
.send()
.await
.context("Failed to send app role login request")?;
if !response.status().is_success() {
// If authentication fails and we have a cached token, invalidate it
if !force_refresh {
GLOBAL_VAULT_TOKEN_CACHE.invalidate(&cache_key).await;
}
return Err(anyhow::anyhow!(
"Vault app role login failed: {} - {}",
response.status(),
response.text().await.unwrap_or_default()
));
}
let auth_response: VaultAuthResponse = response
.json()
.await
.context("Failed to parse Vault auth response")?;
let token = auth_response.auth.client_token;
let lease_duration = auth_response.auth.lease_duration;
// Cache the token with per-entry expiration based on lease duration (90% of lease duration)
let expires_at = Instant::now() + Duration::from_secs((lease_duration * 9) / 10);
let cached_token = CachedToken {
token: token.clone(),View on GitHub (pinned to 6469eb736d)
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
Defensive patterns
Strategy: retry
Validate before calling
// Pre-validate AppRole login outside the data path
let resp = reqwest::Client::new()
.post(format!("{vault_addr}/v1/auth/approle/login"))
.json(&serde_json::json!({"role_id": role_id, "secret_id": secret_id}))
.send().await?;
if !resp.status().is_success() { return Err("invalid role_id/secret_id".into()); } Try / catch
match result {
Err(e) if e.to_string().contains("app role login failed") => {
rotate_secret_id_and_retry().await // credentials are likely expired/revoked
}
other => other,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- No auth method specified for Vault backend
- Vault API returned error status: {} - {}
- Failed to get secret from Vault
- Field '{}' not found in secret
- Both `access_key` and `secret_key` must be provided
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/11cfeed7101fc750.
Report an issue: GitHub.