googleworkspace/cli · error · anyhow::Error

Failed to fetch Discovery Document for {service}/{version}:

Error message

Failed to fetch Discovery Document for {service}/{version}: HTTP {} (tried both standard and $discovery URLs)

What it means

This error is thrown by fetch_discovery_document() after BOTH attempts to download a Google Discovery Document returned a non-2xx HTTP status: first the standard registry URL https://www.googleapis.com/discovery/v1/apis/{service}/{version}/rest, then the fallback https://{service}.googleapis.com/$discovery/rest?version={version} used by newer APIs (Forms, Keep, Meet). The status code in the message is from the second ($discovery) attempt. It means the CLI resolved a service alias but Google's endpoints rejected the request, so no command tree can be built for that service.

Source

Thrown at crates/google-workspace/src/discovery.rs:240

        crate::validate::encode_path_segment(version),
    );

    tracing::debug!(service = %service, version = %version, "Fetching discovery document");
    let client = crate::client::build_client()?;
    let resp = client.get(&url).send().await?;

    let body = if resp.status().is_success() {
        resp.text().await?
    } else {
        // Try the $discovery/rest URL pattern used by newer APIs (Forms, Keep, Meet, etc.)
        let alt_url = format!("https://{service}.googleapis.com/$discovery/rest");
        let alt_resp = client
            .get(&alt_url)
            .query(&[("version", version)])
            .send()
            .await?;
        if !alt_resp.status().is_success() {
            anyhow::bail!(
                "Failed to fetch Discovery Document for {service}/{version}: HTTP {} (tried both standard and $discovery URLs)",
                alt_resp.status()
            );
        }
        alt_resp.text().await?
    };

    // Write to cache
    if let Some(dir) = cache_dir {
        let cache_file = dir.join(format!("{service}_{version}.json"));
        if let Err(e) = tokio::fs::write(&cache_file, &body).await {
            tracing::warn!(error = %e, "Failed to write discovery cache");
        }
    }

    let doc: RestDescription = serde_json::from_str(&body)?;
    Ok(doc)
}

View on GitHub (pinned to a3768d0e82)

Solutions

  1. Run with GOOGLE_WORKSPACE_CLI_LOG=gws=debug to see the exact URLs and statuses tried, then curl both URLs (https://www.googleapis.com/discovery/v1/apis/{service}/{version}/rest and https://{service}.googleapis.com/$discovery/rest?version={version}) to see which status comes back
  2. Check crates/google-workspace/src/services.rs: confirm the alias maps to the exact API name and a currently-published version (compare with the service's Google API docs page)
  3. If a proxy/firewall is involved, allow-list www.googleapis.com and *.googleapis.com, or set HTTPS_PROXY to a proxy that permits those hosts
  4. If the service genuinely serves only the $discovery pattern, verify the host format https://{service}.googleapis.com matches the service's canonical hostname (some services differ, e.g. different API name vs hostname)
  5. Clear the discovery cache only if you suspect corruption: the 24h cache in the configured cache dir is written solely from successful responses, so a stale-bad cache is unlikely — prefer fixing the URL/mapping

Example fix

// crates/google-workspace/src/services.rs — before
("forms", "forms", "v2"), // 404: only v1 exists

// after
("forms", "forms", "v1"),
Defensive patterns

Strategy: retry

Validate before calling

use google_workspace::services;

let alias = "forms";
// Fail fast if the alias is unknown before touching the network
let (api, version) = services::resolve(alias) // or your alias map lookup
    .unwrap_or_else(|| panic!("unknown service alias {alias}"));
// Optional reachability pre-flight
// curl-equivalent: HEAD https://www.googleapis.com/discovery/v1/apis/{api}/{version}/rest

Try / catch

match google_workspace::discovery::fetch_discovery_document(api, version, cache).await {
    Ok(doc) => { /* build commands */ }
    Err(e) if e.to_string().contains("Failed to fetch Discovery Document") => {
        eprintln!("service {api}/{version} unavailable: check alias/version in services.rs and network egress to googleapis.com");
        std::process::exit(2);
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling gws with a service whose alias exists in services.rs but whose version string is wrong or retired (404); using a service that is registered in neither the Discovery registry nor the $discovery endpoint (404); a corporate proxy or captive portal returning 403/407 for googleapis.com; Google API deprecating a version (410 gone); regional firewall returning 403. Note this fires only on HTTP status failures — a connection failure surfaces as a reqwest error from .send() instead.

Common situations: Typo'd or outdated version in crates/google-workspace/src/services.rs after adding a new service alias; running in CI behind an egress proxy that blocks googleapis.com; using a stale service mapping after Google renamed/retired an API; air-gapped or DNS-hijacked networks where the proxy answers with an error page.

Related errors


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