googleworkspace/cli · error · anyhow::Error

Failed to set permissions on token directory '{}': {}

Error message

Failed to set permissions on token directory '{}': {}

What it means

After creating the token directory, `save_to_disk()` applies `chmod 0700` (unix only) and this call failed. The directory exists and was created, but the OS refused the permission change — typical on filesystems that don't support POSIX mode bits (vfat/exFAT/NTFS mounts, some NFS/CIFS configurations) or when the directory was concurrently replaced by something owned by another user.

Source

Thrown at crates/google-workspace-cli/src/token_storage.rs:98

    async fn save_to_disk(&self, map: &HashMap<String, TokenInfo>) -> anyhow::Result<()> {
        let json = serde_json::to_string(map)?;
        let encrypted = crate::credential_store::encrypt(json.as_bytes())?;

        if let Some(parent) = self.file_path.parent() {
            tokio::fs::create_dir_all(parent).await.map_err(|e| {
                anyhow::anyhow!(
                    "Failed to create token directory '{}': {}",
                    sanitize_for_terminal(&parent.display().to_string()),
                    e
                )
            })?;
            #[cfg(unix)]
            {
                use std::os::unix::fs::PermissionsExt;
                tokio::fs::set_permissions(parent, std::fs::Permissions::from_mode(0o700))
                    .await
                    .map_err(|e| {
                        anyhow::anyhow!(
                            "Failed to set permissions on token directory '{}': {}",
                            sanitize_for_terminal(&parent.display().to_string()),
                            e
                        )
                    })?;
            }
        }

        // Write atomically via a sibling .tmp file + rename.
        crate::fs_util::atomic_write_async(&self.file_path, encrypted.as_slice()).await?;

        Ok(())
    }

    // Helper to join scopes consistently for cache keys
    fn cache_key(scopes: &[&str]) -> String {
        let mut s: Vec<&str> = scopes.to_vec();
        s.sort_unstable();

View on GitHub (pinned to a3768d0e82)

Solutions

  1. Move the config dir to a POSIX filesystem: `GOOGLE_WORKSPACE_CLI_CONFIG_DIR=$HOME/.config/gws` with $HOME on ext4/xfs/apfs.
  2. On SELinux systems, check for denials (`ausearch -m avc -ts recent`) and adjust the context for the config path.
  3. Ensure no concurrent gws/auth processes are racing on the same dir; retry the login.
  4. As a last resort on non-POSIX mounts, accept that mode bits cannot be enforced and use an encrypted-filesystem path instead (the token file is itself AES-GCM encrypted, but directory perms are defense-in-depth).

Example fix

# before — config dir on a Windows mount under WSL
export GOOGLE_WORKSPACE_CLI_CONFIG_DIR=/mnt/c/Users/me/gws
 gws auth login  # -> Failed to set permissions on token directory '...': Operation not supported

# after — keep secrets on the Linux filesystem
export GOOGLE_WORKSPACE_CLI_CONFIG_DIR="$HOME/.config/gws"
gws auth login
Defensive patterns

Strategy: try-catch

Validate before calling

// Detect non-POSIX mounts before relying on chmod
fn supports_posix_perms(p: &std::path::Path) -> bool {
    use std::os::unix::fs::MetadataExt;
    match std::fs::metadata(p) {
        Ok(m) => m.mode() & 0o777 != 0 || true, // best-effort: probe with a real chmod instead
        Err(_) => false,
    }
}

Try / catch

if let Err(e) = tokio::fs::set_permissions(parent, perms).await {
    // 0700 is defense-in-depth; the token file itself is AES-GCM encrypted.
    // Log loudly but do not fail the login on filesystems without POSIX bits.
    tracing::warn!(error = %e, dir = %parent.display(), "cannot enforce 0700 on token dir (non-POSIX fs?)");
}

Prevention

When it happens

Trigger: `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` pointed at a FAT/NTFS-mounted USB or Windows-drive mount; an NFS home with root-squash oddities; another process (or admin tool) chowning/replacing the dir between create and chmod; SELinux/AppArmor denying chmod.

Common situations: WSL with config on /mnt/c; mounted external drives; hardened SELinux hosts; shared multi-user systems.

Related errors


AI-assisted analysis of googleworkspace/cli@a3768d0e82 (2026-08-16). Data as JSON: /api/errors/c53b850adc151dd6. Report an issue: GitHub.