googleworkspace/cli · error · anyhow::Error

Cannot read {}: {e}

Error message

Cannot read {}: {e}

What it means

`load_client_config()` failed to read `<config dir>/client_secret.json` with `std::fs::read_to_string`. The error names the exact path tried. Overwhelmingly this is NotFound — the user has not completed OAuth client setup yet — but it can also be permission-denied on the file or a directory in the path.

Source

Thrown at crates/google-workspace-cli/src/oauth_config.rs:94

    };

    let path = client_config_path();
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent)?;
    }

    let json = serde_json::to_string_pretty(&config)?;
    crate::fs_util::atomic_write(&path, json.as_bytes())
        .map_err(|e| anyhow::anyhow!("Failed to write client config: {e}"))?;

    Ok(path)
}

/// Loads OAuth client configuration from the standard Google Cloud Console format.
pub fn load_client_config() -> anyhow::Result<InstalledConfig> {
    let path = client_config_path();
    let data = std::fs::read_to_string(&path)
        .map_err(|e| anyhow::anyhow!("Cannot read {}: {e}", path.display()))?;
    let file: ClientSecretFile = serde_json::from_str(&data)
        .map_err(|e| anyhow::anyhow!("Invalid client_secret.json format: {e}"))?;
    Ok(file.installed)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_save_load_round_trip() {
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("client_secret.json");

        let config = ClientSecretFile {
            installed: InstalledConfig {
                client_id: "test-id.apps.googleusercontent.com".to_string(),
                client_secret: "GOCSPX-test".to_string(),

View on GitHub (pinned to a3768d0e82)

Solutions

  1. Run `gws auth setup` (or `gws auth login` interactively) so a client_secret.json is saved, or copy your existing one to the path shown in the error.
  2. Verify the path: `ls -l <path-from-error>` — if it's not where your secret lives, set `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` to the right directory.
  3. Fix permissions (`chmod 600`, correct owner) if the file exists but is unreadable.
  4. Alternatively skip the file entirely: export GOOGLE_WORKSPACE_CLI_CLIENT_ID / GOOGLE_WORKSPACE_CLI_CLIENT_SECRET, or GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE pointing at the downloaded secret.

Example fix

# before
gws auth login
# -> Cannot read /home/me/.config/gws/client_secret.json: No such file or directory

# after — put the downloaded Desktop-app secret where the loader looks
gws auth setup   # interactive wizard writes it
# or manually:
mkdir -p ~/.config/gws && cp ~/Downloads/client_secret_*.json ~/.config/gws/client_secret.json
gws auth login
Defensive patterns

Strategy: validation

Validate before calling

// Check before attempting an authenticated flow
let cfg = gws::oauth_config::client_config_path();
if !cfg.exists() {
    eprintln!("no client config at {} — run `gws auth setup` first", cfg.display());
}

Try / catch

match load_client_config() {
    Ok(c) => c,
    Err(e) if e.to_string().contains("Cannot read") => {
        eprintln!("client_secret.json missing/unreadable — run `gws auth setup`, copy one in, or set GOOGLE_WORKSPACE_CLI_CLIENT_ID/SECRET");
        std::process::exit(2);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Running `gws auth login` on a fresh machine before `gws auth setup` saved a client config; `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` pointing at a different directory than the one the config was saved in; the file having 000/root-only permissions; passing a credentials file via env var but expecting this loader to find it (it only reads the fixed path).

Common situations: New installs that skip the setup wizard; multi-account setups where the config dir was switched; shared machines where another user's umask locked the file; CI with a clean HOME.

Related errors


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