xai-org/grok-build · error · OidcError
OidcError::SaveAuth
Error message
OidcError::SaveAuth
What it means
After a successful OIDC token exchange, `run_login_flow_with_config` persists the built GrokAuth via `auth_manager.update(auth)`. This error wraps any failure of that persistence step (writing credentials to storage), so the login technically succeeded against the IDP but the credentials could not be saved.
Solutions
- Check write permissions and free space on the credentials storage location (~/.config or the tool's auth dir).
- Remove a corrupted credentials file and re-run login.
- Ensure no other process holds a lock on the auth store; close concurrent sessions and retry.
- In containers/CI, set HOME/XDG_CONFIG_HOME to a writable path and mount a volume for the credentials.
- Inspect the wrapped error string (in OidcError::SaveAuth) for the exact IO/serialization cause.
Example fix
// before: read-only home in container $ docker run --read-only grok login # SaveAuth fails // after $ docker run -v grok-config:/root/.config grok login # writable volume, save succeeds
Defensive patterns
Strategy: validation
Validate before calling
// preflight: verify the credentials directory is writable before login
let auth_dir = dirs::home_dir().unwrap().join(".config/grok");
std::fs::create_dir_all(&auth_dir)?;
let probe = auth_dir.join(".write_probe");
std::fs::write(&probe, b"ok")?;
std::fs::remove_file(&probe)?; Type guard
fn is_save_auth(err: &anyhow::Error) -> bool {
err.downcast_ref::<OidcError>()
.map_or(false, |e| matches!(e, OidcError::SaveAuth(_)))
} Try / catch
if is_save_auth(&err) {
eprintln!("Login succeeded but saving credentials failed. Check permissions/free space on the auth store, then retry.");
} Prevention
- Mount a writable volume for the config/auth dir in containers
- Check disk space and quota before running logins in CI
- Delete corrupted credentials files instead of retrying over them
- Avoid running concurrent sessions that contend for the auth store lock
When it happens
Trigger: auth_manager.update(auth).await returns Err in run_login_flow_with_config after build_grok_auth/enrich_auth_inline succeed — wrapped as OidcError::SaveAuth(e.to_string()).
Common situations: Credentials file/directory not writable (permissions, read-only FS, full disk); storage locked by a concurrent session; corrupted existing credentials file failing to serialize; HOME/XDG path misconfigured in CI containers; disk quota exceeded.
Related errors
- failed to save external auth credentials
- FS_OTHER
- AudienceMismatch
- auth entry has no oidc_client_id — cannot refresh expired…
- auth entry has no oidc_issuer — cannot refresh expired…
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/4fd201246f35ca5c.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codegen/xai-grok-shell/src/auth/oidc/login.rs:539
let user_info = extract_user_info(
tokens.id_token.as_deref(),
&discovery,
&oidc.issuer,
&oidc.client_id,
&nonce,
resolved_principal_type.as_deref(),
resolved_principal_id.as_deref(),
resolved_team_id,
)
.await?;
tracing::debug!(user_id = %user_info.user_id, "OIDC: extracted user info");
let mut auth = build_grok_auth(tokens, user_info, &oidc.issuer, &oidc.client_id);
auth_manager.enrich_auth_inline(&mut auth).await;
let auth = auth_manager
.update(auth)
.await
.map_err(|e| anyhow::Error::new(OidcError::SaveAuth(e.to_string())))?;
tracing::info!(user_id = %auth.user_id, "OIDC: login complete, credentials saved");
Ok((auth, true))
}
/// Successful OIDC callback payload.
#[derive(Debug, PartialEq, Eq)]
struct Callback {
code: String,
state: String,
}
/// Result from the OIDC callback: either a [`Callback`] or an IdP error message.
type CallbackResult = Result<Callback, String>;
#[cfg(test)]
mod tests {
use super::super::test_helpers::*;View on GitHub (pinned to bc7f02eddd)