zeroclaw-labs/zeroclaw · error · anyhow::Error
Gemini API error: {}
Error message
Gemini API error: {} What it means
Gemini answered HTTP 200 but the JSON body carries a top-level error object (GenerateContentResponse.error). ZeroClaw treats this in-band error as fatal and surfaces err.message; some auth restrictions and abuse-policy blocks are reported this way despite the success status.
Source
Thrown at crates/zeroclaw-providers/src/gemini.rs:1366
project.as_deref(),
oauth_token.as_deref(),
)?
.send()
.await?;
} else {
anyhow::bail!("Gemini API error ({status}): {error_text}");
}
}
if !response.status().is_success() {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
anyhow::bail!("Gemini API error ({status}): {error_text}");
}
let result: GenerateContentResponse = response.json().await?;
if let Some(err) = &result.error {
anyhow::bail!("Gemini API error: {}", err.message);
}
let result = result.into_effective_response();
if let Some(err) = result.error {
anyhow::bail!("Gemini API error: {}", err.message);
}
let usage = result
.usage_metadata
.and_then(Self::token_usage_from_metadata);
let text = result
.candidates
.and_then(|c| c.into_iter().next())
.and_then(|c| c.content)
.and_then(|c| c.effective_text())
.ok_or_else(|| {
::zeroclaw_log::record!(
ERROR,View on GitHub (pinned to 88bb9c8533)
Solutions
- Match on the message: API_KEY_INVALID -> recreate the key without referrer/IP restrictions; safety block -> rephrase input or adjust safety settings where policy allows
- Log err.message verbatim for support escalation
- Retry only when the message clearly indicates a transient condition
Example fix
// after - surface the in-band message for routing decisions
if let Err(e) = provider.chat_with_system(None, prompt, model, None).await {
let msg = e.to_string();
if msg.contains("API_KEY_INVALID") {
return Err(anyhow::anyhow!("recreate GEMINI_API_KEY without referrer restrictions"));
}
return Err(e);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: catch restricted keys before chat calls
async fn gemini_key_unrestricted() -> anyhow::Result<()> {
// a 1-token test request surfaces API_KEY_INVALID early
Ok(())
} Type guard
fn is_gemini_in_band_error(e: &anyhow::Error) -> bool {
e.to_string().starts_with("Gemini API error: ")
} Try / catch
if let Err(e) = provider.chat_with_system(None, prompt, model, None).await {
let msg = e.to_string();
if msg.contains("API_KEY_INVALID") {
return Err(anyhow::anyhow!("recreate the key without referrer/IP restrictions"));
}
if msg.contains("safety") || msg.contains("block") {
return Err(anyhow::anyhow!("input tripped a content filter: {msg}"));
}
return Err(e);
} Prevention
- Create server-side API keys without HTTP referrer restrictions
- Run a smoke-test prompt at startup to fail fast on in-band auth errors
- Log err.message verbatim; it is the only diagnostic you get on a 200
- Do not blind-retry in-band policy errors - they are deterministic
When it happens
Trigger: HTTP-referrer or IP-restricted API keys used from a server (key validates at routing, fails at execution); content that trips safety filters; endpoints that fold stream failures into a single error field.
Common situations: API key created with browser referrer restrictions then used server-side; safety-filter blocks on borderline prompts; partially rolled-out model endpoints.
Related errors
- QQ channel requires the `channel-qq` feature
- Lark channel requires the `channel-lark` feature
- DingTalk channel requires the `channel-dingtalk` feature
- WeCom channel requires the `channel-wecom` feature
- WeCom WebSocket channel requires the `channel-wecom-ws` feat
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/ad371d919b848608.
Report an issue: GitHub.