zeroclaw-labs/zeroclaw · error
Embedding API error {status}: {text}
Error message
Embedding API error {status}: {text} What it means
OpenAiEmbedding::embed POSTs {"model", "input": texts} with a Bearer token to {base_url}/v1/embeddings (or {base_url}/embeddings when the base URL already carries an explicit path). Any non-2xx response is turned into this error, embedding the HTTP status code and the provider's response body. It is the provider rejecting the request — auth, model, payload, rate limits, or proxy — and the response text usually names the exact reason.
Source
Thrown at crates/zeroclaw-memory/src/embeddings.rs:151
let body = serde_json::json!({
"model": self.model,
"input": texts,
});
let resp = self
.http_client()
.post(self.embeddings_url())
.header("Authorization", format!("Bearer {}", self.api_key))
.header("Content-Type", "application/json")
.json(&body)
.send()
.await?;
if !resp.status().is_success() {
let status = resp.status();
let text = resp.text().await.unwrap_or_default();
anyhow::bail!("Embedding API error {status}: {text}");
}
let json: serde_json::Value = resp.json().await?;
let data = json.get("data").and_then(|d| d.as_array()).ok_or_else(|| {
::zeroclaw_log::record!(
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
.with_outcome(::zeroclaw_log::EventOutcome::Failure),
"embedding response missing 'data' field"
);
anyhow::Error::msg("Invalid embedding response: missing 'data'")
})?;
let mut embeddings = Vec::with_capacity(data.len());
for item in data {
let embedding = item
.get("embedding")
.and_then(|e| e.as_array())View on GitHub (pinned to 88bb9c8533)
Solutions
- Read the status and body in the message first: 401/403 → fix the API key; 400/404 → fix model name, base_url path, or dimensions to match the endpoint; 429 → slow down/backoff or raise quota; 5xx → retry later.
- Verify the endpoint by curl-ing {base_url}/v1/embeddings with the same model and one short input; the response body must contain a data[].embedding array.
- Check the runtime proxy configuration for memory.embeddings if a proxy sits in the path (407/502/503 usually come from it, not the provider).
- Batch smaller: split the texts slice and retry so per-request input limits are not hit.
- If embeddings are optional for your flow, fall back to NoopEmbedding (keyword-only) explicitly rather than letting recall fail.
Example fix
// before
let emb = OpenAiEmbedding::new("http://localhost:9999", &key, "text-embedding-3-large", 1536);
let v = emb.embed_one(text).await?; // Embedding API error 404 Not Found: ...
// after: model and base path that the endpoint actually serves
let emb = OpenAiEmbedding::new("https://api.openai.com", &key, "text-embedding-3-small", 1536);
let v = emb.embed_one(text).await?; Defensive patterns
Strategy: retry
Validate before calling
null
Try / catch
// Distinguish transient (retry) from permanent (fix config) statuses
const MAX_TRIES: u32 = 3;
for attempt in 1..=MAX_TRIES {
match provider.embed(&texts).await {
Ok(v) => return Ok(v),
Err(e) if e.to_string().contains("Embedding API error 429") => {
tokio::time::sleep(std::time::Duration::from_secs(2u64.pow(attempt))).await;
}
Err(e) if e.to_string().contains("Embedding API error 5") => {
tokio::time::sleep(std::time::Duration::from_secs(attempt as u64)).await;
}
Err(e) => return Err(e), // 4xx: fix api key / model / base_url, do not retry
}
}
bail!("embedding provider unavailable after {MAX_TRIES} retries") Prevention
- Smoke-test the provider at startup: embed_one("ping") and fail fast with a clear config error before the first real recall.
- Keep base_url, model, and dims as one versioned unit in config — they must match the endpoint together.
- Run recall/store paths against NoopEmbedding in environments without network, so missing embeddings degrade to keyword search by design.
- Log status+body verbatim on failure (this error already includes both) and alert on 401/403 separately from 429/5xx.
- Batch embedding requests below the provider's per-request input limit.
When it happens
Trigger: Calling embed/embed_one on OpenAiEmbedding (recall/store paths in memory that need vectors) with: a wrong/missing API key (401), a model name the endpoint does not serve or a model/dims mismatch (400/404), a base_url pointing at the wrong path or a non-OpenAI-compatible server (404), too many/too long texts in one batch (400 payload limits), provider rate limiting or exhausted quota (429), or transient 5xx/proxy failures — note the client is built via build_runtime_proxy_client("memory.embeddings"), so a misconfigured runtime proxy surfaces here too.
Common situations: Swapping embedding providers (e.g. a local OpenAI-compatible server) without updating base_url/model/dims together; expired or rotated API keys; CI environments without the proxy env vars the runtime expects; nightly batch jobs that exceed rate limits after the corpus grows; model renamed by the vendor.
Related errors
- Composio v3 API error: {err}
- Composio v3 connected accounts lookup failed: {err}
- providers.models.ollama.{alias}.model uses ':cloud', but no
- AgentScopedMemory refuses purge_namespace: cross-agent bulk
- memory consolidation write denied by policy: {e}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/30d35625d784cbd6.
Report an issue: GitHub.