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

  1. Check permissions on the directory containing auth.json (should be writable by the current user): `ls -ld ~/.grok`.
  2. Verify the disk is not full (`df -h`) and the filesystem is mounted read-write (`mount | grep ...`).
  3. Ensure $GROK_HOME/$HOME point to an existing, writable directory; create `~/.grok` if missing.
  4. 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

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


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/616a9a7afe455320. Report an issue: GitHub.