zeroclaw-labs/zeroclaw · error · anyhow::Error
GLM API error: {error}
Error message
GLM API error: {error} What it means
chat_with_system POSTed to {base_url}/chat/completions and got a non-2xx; the raw response body is embedded verbatim after 'GLM API error:'. The body distinguishes bad JWT/key (401), unknown model, rate limit (429), and context overflow.
Source
Thrown at crates/zeroclaw-providers/src/glm.rs:196
let request = ChatRequest {
model: model.to_string(),
messages,
temperature,
};
let url = format!("{}/chat/completions", self.base_url);
let response = self
.http_client()
.post(&url)
.header("Authorization", format!("Bearer {token}"))
.json(&request)
.send()
.await?;
if !response.status().is_success() {
let error = response.text().await?;
anyhow::bail!("GLM API error: {error}");
}
let chat_response: ChatResponse = response.json().await?;
chat_response
.choices
.into_iter()
.next()
.map(|c| c.message.content)
.ok_or_else(|| {
::zeroclaw_log::record!(
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
.with_outcome(::zeroclaw_log::EventOutcome::Failure),
"glm: empty choices in response"
);
anyhow::Error::msg("No response from GLM")
})View on GitHub (pinned to 88bb9c8533)
Solutions
- Read the embedded body: 401 -> verify the id.secret key; model-not-found -> use a current model id; 429 -> back off
- Verify the system clock (JWT exp depends on it)
- Restore the default base_url https://api.z.ai/api/paas/v4 unless using a compatible gateway
- Retry with backoff only for 429/5xx-class bodies
Example fix
// before
let text = provider.chat_with_system(None, prompt, model, temp).await?;
// after
let text = match provider.chat_with_system(None, prompt, model, temp).await {
Ok(t) => t,
Err(e) if e.to_string().contains("429") => {
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
provider.chat_with_system(None, prompt, model, temp).await?
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: try-catch
Validate before calling
fn glm_credentials_ready() -> bool {
std::env::var("GLM_API_KEY").map(|k| glm_key_valid(&k)).unwrap_or(false)
} Type guard
fn is_glm_http_error(e: &anyhow::Error) -> bool {
e.to_string().starts_with("GLM API error: ")
} Try / catch
match provider.chat_with_system(None, prompt, model, temp).await {
Ok(t) => Ok(t),
Err(e) => {
let msg = e.to_string();
if msg.contains("429") || msg.contains("500") {
backoff_and_retry().await
} else {
Err(anyhow::anyhow!("GLM call failed: {msg}")) // auth/model: fix config
}
}
} Prevention
- Keep NTP healthy on hosts issuing GLM JWTs (3.5-minute expiry)
- Verify model ids against the current GLM catalog when upgrading
- Retain the embedded body in logs; it is the authoritative diagnostic
- Wrap batch jobs with per-request backoff for 429s
When it happens
Trigger: Calling chat_with_system with an unsupported model id for the GLM endpoint; a mis-signed or clock-skewed JWT (tokens expire after 3.5 minutes); 429 rate limiting; a base_url override pointing at an incompatible proxy.
Common situations: Upstream model rename (glm-4 era ids retired); host clock skew breaking the exp claim; corporate proxy rewriting responses; quota exhausted on the Z.ai plan.
Related errors
- GLM API key not set or invalid format. Expected 'id.secret'.
- Generated image download failed with HTTP {}
- Jira get_ticket failed ({status}): {}
- Jira search_tickets failed ({status}): {}
- Jira comment_ticket failed ({status}): {}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/8c888483e090e16e.
Report an issue: GitHub.