xai-org/grok-build · error
failed to open {}: {e}
Error message
failed to open {}: {e} What it means
`write_json_atomic` persists refreshed tokens by writing JSON to a temp file next to the target and renaming it into place. This error is thrown when the temp file cannot be opened for writing. On Unix the file is created with mode 0600 so credentials are protected. It wraps the underlying `std::io::Error` (permission denied, read-only filesystem, missing parent dir, etc.).
Source
Thrown at crates/codegen/xai-grok-workspace/src/hub_auth/mod.rs:364
/// the truncate-in-place corruption window when the long-lived binary rewrites
/// auth.json.
fn write_json_atomic(path: &Path, value: &serde_json::Value) -> anyhow::Result<()> {
use std::io::Write;
let json = serde_json::to_string_pretty(value)?;
let tmp = path.with_extension(format!("json.{}.tmp", std::process::id()));
let mut opts = std::fs::OpenOptions::new();
opts.write(true).create(true).truncate(true);
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
opts.mode(0o600);
}
let mut file = opts
.open(&tmp)
.map_err(|e| anyhow::anyhow!("failed to open {}: {e}", tmp.display()))?;
file.write_all(json.as_bytes())?;
file.sync_all()?;
drop(file);
#[cfg(windows)]
let _ = std::fs::remove_file(path);
if let Err(e) = std::fs::rename(&tmp, path) {
let _ = std::fs::remove_file(&tmp);
return Err(anyhow::anyhow!("failed to replace {}: {e}", path.display()));
}
Ok(())
}
/// Build a hub auth provider for `hub_url`. `auth_config` overrides
/// the default credential path (`~/.grok/auth.json`).
///
/// `refresh_cfg.enabled` selects the workspace-owned proactive refresher;View on GitHub (pinned to bc7f02eddd)
Solutions
- Check permissions on the directory containing auth.json (should be writable by the current user): `ls -ld ~/.grok`.
- Verify the disk is not full (`df -h`) and the filesystem is mounted read-write (`mount | grep ...`).
- Ensure $GROK_HOME/$HOME point to an existing, writable directory; create `~/.grok` if missing.
- On Windows, check that no antivirus/backup process is locking the temp file.
Defensive patterns
Strategy: try-catch
Try / catch
// The error wraps the io::Error; downcast to inspect the OS error kind
match write_refreshed_token(&auth_path, &scope_key, &event) {
Ok(()) => {},
Err(e) => {
if let Some(io_err) = e.chain().find_map(|c| c.downcast_ref::<std::io::Error>()) {
match io_err.kind() {
std::io::ErrorKind::PermissionDenied => eprintln!("fix perms on {}", auth_path.display()),
std::io::ErrorKind::StorageFull => eprintln!("disk full"),
_ => eprintln!("persist failed: {io_err}"),
}
}
}
} Prevention
- Ensure ~/.grok exists and is owned/writable by the running user before starting sessions.
- Keep a few MB of free disk space; token refresh writes fail on full disks.
- Avoid running the CLI as a different user (sudo) than the one that owns auth.json.
- On Windows, exclude the credentials directory from aggressive AV/backup locking.
When it happens
Trigger: `write_refreshed_token` -> `write_json_atomic` when `OpenOptions::create(true).write(true).truncate(true).open(&tmp)` fails: the directory does not exist, the process lacks write permission, the filesystem is full/read-only, or an AV/another process holds a conflicting lock on Windows.
Common situations: auth.json directory owned by root or another user; $HOME on a read-only mount or full disk; container running as non-root with a volume mounted read-only; corrupted GROK_HOME path.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- hook write-deny ensure failed: {e}
- failed to set mcp preferences permissions: {e}
- failed to read {}: {e}
- failed to create chrome trace {:?}: {}
- failed to read {}: {e}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/616a9a7afe455320.
Report an issue: GitHub.