googleworkspace/cli · error · GwsError
Failed to parse calendar list: {e}
Error message
Failed to parse calendar list: {e} What it means
The calendarList request returned a success status, but response.json() could not deserialize the body as JSON. So the transport worked, the server said OK, yet the payload is not the expected JSON document — usually an HTML page or an empty/garbled body from an intermediary, since the Calendar API itself reliably returns JSON on 2xx.
Source
Thrown at crates/google-workspace-cli/src/helpers/calendar.rs:283
.bearer_auth(&token)
.send()
.await
.map_err(|e| GwsError::Other(anyhow::anyhow!("Failed to list calendars: {e}")))?;
if !list_resp.status().is_success() {
let err = list_resp.text().await.unwrap_or_default();
return Err(GwsError::Api {
code: 0,
message: err,
reason: "calendarList_failed".to_string(),
enable_url: None,
});
}
let list_json: Value = list_resp
.json()
.await
.map_err(|e| GwsError::Other(anyhow::anyhow!("Failed to parse calendar list: {e}")))?;
let calendars = list_json
.get("items")
.and_then(|i| i.as_array())
.cloned()
.unwrap_or_default();
// 2. For each calendar, fetch events concurrently
use futures_util::stream::{self, StreamExt};
// Pre-filter calendars and collect owned data to avoid lifetime issues
struct CalInfo {
id: String,
summary: String,
}
let filtered_calendars: Vec<CalInfo> = calendars
.iter()
.filter_map(|cal| {View on GitHub (pinned to a3768d0e82)
Solutions
- Reproduce the raw body: curl -s 'https://www.googleapis.com/calendar/v3/users/me/calendarList' -H "Authorization: Bearer $TOKEN" | head -c 400 — if it is HTML, fix the proxy/network
- Bypass or correctly configure the intercepting proxy / captive portal
- If the body looks like valid JSON, report a bug with the body snippet
Example fix
// before: blind json() losing the payload on failure
let list_json: Value = list_resp.json().await.map_err(|e| GwsError::Other(anyhow::anyhow!("Failed to parse calendar list: {e}")))?;
// after: capture the body for diagnosis
let body = list_resp.text().await.unwrap_or_default();
let list_json: Value = serde_json::from_str(&body).map_err(|e| {
GwsError::Other(anyhow::anyhow!("calendarList body was not JSON ({e}): {}", &body[..body.len().min(200)]))
})?; Defensive patterns
Strategy: try-catch
Validate before calling
// Fetch text first, validate JSON shape before trusting it
let body = resp.text().await?;
let v: serde_json::Value = serde_json::from_str(&body)?;
anyhow::ensure!(v.get("items").map(|i| i.is_array()).unwrap_or(false) || v.get("kind").is_some(),
"unexpected calendarList payload (not a Calendar API document)"); Type guard
fn is_calendar_list_doc(v: &serde_json::Value) -> bool {
v.is_object() && (v.get("items").is_some_and(|i| i.is_array()) || v.get("kind").is_some())
} Try / catch
let body = resp.text().await.unwrap_or_default();
let parsed: serde_json::Value = match serde_json::from_str(&body) {
Ok(v) => v,
Err(e) => {
// non-JSON 2xx body: proxy/captive portal — log first 200 bytes and surface a network-config error
anyhow::bail!("calendarList returned non-JSON body ({e}): {}", &body.chars().take(200).collect::<String>())
}
}; Prevention
- Never assume 2xx implies JSON on networks you do not control — parse defensively
- Check Content-Type and body prefix when responses come through proxies
- Log a short body excerpt on parse failures; it turns a mystery into an obvious captive-portal/proxy page
When it happens
Trigger: Captive portal / proxy returning an HTML terms page with 200; a transparent proxy appending junk; empty 200 body from a misrouting gateway; response gzip/charset mangling; API edge returning an empty body on partial outage.
Common situations: Corporate web filters intercepting Google domains; hotel/airport Wi-Fi captive portals; flaky home routers with 'web acceleration'; rare Google frontend incidents.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to list calendars: {e}
- Failed to serialize seed payload for idempotency key: {e}
- Failed to parse message: {e}
- Failed to parse sendAs response: {e}
- Failed to parse People API response: {e}
AI-assisted analysis of googleworkspace/cli@a3768d0e82 (2026-08-16).
Data as JSON: /api/errors/0949b4bb1eab65fc.
Report an issue: GitHub.