googleworkspace/cli · error
key file exists but is corrupt
Error message
key file exists but is corrupt
What it means
File-backend key creation is race-safe: it writes the base64 key with an exclusive create, and if another process won the race (AlreadyExists), it re-reads the winner's file. read_key_file returns None when the content cannot be decoded to exactly 32 bytes (unreadable, invalid base64, or wrong decoded length), so this error means the concurrently-created key file exists but does not parse — a genuinely corrupt key file, not just a lost race.
Source
Thrown at crates/google-workspace-cli/src/credential_store.rs:337
);
}
}
}
}
// --- 2. File fallback ------------------------------------------------
if let Some(key) = read_key_file(key_file) {
return Ok(key);
}
// --- 3. Generate new key, save to file (race-safe) -------------------
let key = generate_random_key();
let b64_key = STANDARD.encode(key);
match save_key_file_exclusive(key_file, &b64_key) {
Ok(()) => Ok(key),
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
// Another process created the file first — use their key.
read_key_file(key_file).ok_or_else(|| anyhow::anyhow!("key file exists but is corrupt"))
}
Err(e) => Err(e.into()),
}
}
/// Returns the encryption key, generating and persisting one if it doesn't exist.
///
/// The key is cached in-process via `OnceLock` so it is only read from disk once.
/// Backend selection is controlled by `GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND`.
fn get_or_create_key() -> anyhow::Result<[u8; 32]> {
static KEY: OnceLock<[u8; 32]> = OnceLock::new();
if let Some(key) = KEY.get() {
return Ok(*key);
}
#[cfg(not(test))]
let backend = KeyringBackend::from_env();View on GitHub (pinned to a3768d0e82)
Solutions
- Delete the key file under the config dir (e.g. ~/.config/gws/encryption key file) and rerun — a fresh one will be generated (existing encrypted credentials must then be re-created via logout/login)
- Check disk space and filesystem writability on the config dir
- Serialize first-run initialization when multiple processes share the same HOME
Example fix
$ rm ~/.config/gws/gws.key # then rerun gws auth login
Defensive patterns
Strategy: validation
Validate before calling
// Before relying on the file-backend key, validate it parses to 32 bytes
fn key_file_ok(path: &std::path::Path) -> bool {
use base64::{engine::general_purpose::STANDARD, Engine as _};
std::fs::read_to_string(path).ok()
.and_then(|s| STANDARD.decode(s.trim()).ok())
.is_some_and(|k| k.len() == 32)
} Prevention
- Do not run two first-time initializations concurrently against a shared HOME (CI matrix)
- Exclude the gws config dir from sync clients
- If a key-file error appears, deleting the key file is always safe — worst case you re-login
When it happens
Trigger: Two gws processes starting simultaneously on a first run where the winner's write landed truncated (0-byte) or the file was created empty by an external tool; disk-full causing a partial key file; someone hand-edited the key file mid-init.
Common situations: Parallel first-run invocations (CI matrix sharing a HOME, two terminals); sync clients (Dropbox) touching the key file during creation; NFS/overlayfs quirks truncating the exclusive create.
Related errors
AI-assisted analysis of googleworkspace/cli@a3768d0e82 (2026-08-16).
Data as JSON: /api/errors/2345e789aa44a1fd.
Report an issue: GitHub.