{"record":{"id":"d9dcf3e3be363078","repo":"xai-org/grok-build","slug":"failed-to-save-credentials-e","errorCode":null,"errorMessage":"Failed to save credentials: {e}","messagePattern":"Failed to save credentials: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/auth/device_code.rs","lineNumber":492,"sourceCode":"        team_id,\n        team_name: None,\n        team_role: None,\n        user_blocked_reason: None,\n        team_blocked_reasons: vec![],\n        coding_data_retention_opt_out: crate::auth::default_coding_data_retention_opt_out(),\n        has_grok_code_access: None,\n        refresh_token: tokens.refresh_token.clone(),\n        expires_at: tokens.expires_in.map(|s| now + Duration::seconds(s)),\n        oidc_issuer: Some(issuer.to_owned()),\n        oidc_client_id: Some(client_id.to_owned()),\n    };\n\n    auth_manager.enrich_auth_inline(&mut auth).await;\n\n    auth_manager\n        .update(auth)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to save credentials: {e}\"))\n}\n\n/// Decode JWT payload without signature verification.\n/// Returns (sub, Option<email>).\nfn decode_jwt_claims(jwt: &str) -> (String, Option<String>) {\n    use base64::Engine;\n    let parts: Vec<&str> = jwt.splitn(3, '.').collect();\n    if parts.len() < 2 {\n        return (String::new(), None);\n    }\n    let payload = match base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(parts[1]) {\n        Ok(bytes) => bytes,\n        Err(_) => return (String::new(), None),\n    };\n    let claims: IdTokenClaims = match serde_json::from_slice(&payload) {\n        Ok(claims) => claims,\n        Err(_) => return (String::new(), None),\n    };","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/auth/device_code.rs#L474-L510","documentation":"build_auth finishes device-code login by persisting the enriched Auth via auth_manager.update; a persistence failure is wrapped as 'Failed to save credentials'.","triggerScenarios":"auth_manager.update returning Err — credential store unwritable, keychain/keyring unavailable, locked or corrupt credentials file, or storage backend rejecting the update.","commonSituations":"Read-only home dir or disk full in CI containers, headless Linux without a keyring service, file permissions denying access to the credentials path, concurrent login runs corrupting the store.","solutions":["Check the underlying update error in the message chain for the concrete cause (permissions, keyring, disk)","Verify the credentials file/directory is writable (or the OS keyring is reachable in headless environments)","Set an explicit credentials-file path the process can write if the default location is restricted","Ensure no concurrent login processes are racing on the same store"],"exampleFix":"// before\nXDG_DATA_HOME unset -> keyring missing in headless CI\n// after\nexport XDG_DATA_HOME=/tmp/xdg  # writable file-backed credential store\n<run device-code login again>","handlingStrategy":"try-catch","validationCode":"// pre-check storage writability\nlet dir = credentials_dir()?;\nassert!(dir.exists() && !dir.permissions().readonly(), \"credential store not writable\");","typeGuard":null,"tryCatchPattern":"match build_auth(...).await {\n    Err(e) if e.to_string().contains(\"Failed to save credentials\") => {\n        eprintln!(\"persisting auth failed: {e}; check keyring/permissions\");\n        // fall back to a file-based credentials path\n    }\n    other => other?,\n}","preventionTips":["Provision a writable credentials path (or keyring service) in CI/containers before login","Avoid concurrent logins against the same credential store","Surface chained error causes (source) when logging this failure"],"tags":["auth","persistence","credentials"],"backgroundTag":"credentials-save-failed","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}