zeroclaw-labs/zeroclaw · error
Qdrant upsert failed ({status}): {text}
Error message
Qdrant upsert failed ({status}): {text} What it means
store_with_agent() upserts a freshly generated UUID point (vector + payload) via PUT /collections/{c}/points?wait=true. A non-2xx on that upsert raises this error. Because the point id is generated per call, blindly retrying store() creates a duplicate entry — fix the root cause or dedupe instead.
Source
Thrown at crates/zeroclaw-memory/src/qdrant.rs:977
"payload": payload
}]
});
let resp = self
.request(
reqwest::Method::PUT,
&format!("/collections/{}/points", self.collection),
)
.query(&[("wait", "true")])
.json(&upsert_body)
.send()
.await
.context("failed to upsert point to Qdrant")?;
if !resp.status().is_success() {
let status = resp.status();
let text = resp.text().await.unwrap_or_default();
anyhow::bail!("Qdrant upsert failed ({status}): {text}");
}
Ok(())
}
async fn recall_for_agents(
&self,
allowed_agent_ids: &[&str],
query: &str,
limit: usize,
session_id: Option<&str>,
since: Option<&str>,
until: Option<&str>,
) -> Result<Vec<MemoryEntry>> {
// Empty allowlist = no agent filter (matches the wrapper's
// semantics; see the SQL backends).
if allowed_agent_ids.is_empty() {
return self.recall(query, limit, session_id, since, until).await;View on GitHub (pinned to 88bb9c8533)
Solutions
- Match dimensions (GET /collections/{c} -> vectors.size vs provider dims); export memories, recreate the collection under a new name, restart to rebuild, re-embed
- 413: store smaller content (split long documents before store)
- 5xx: check Qdrant memory/optimizer settings and retry once healthy; make retries idempotent by keying points on a content hash if you control the writer
- 404: recreate the collection and restart the process
Defensive patterns
Strategy: validation
Validate before calling
// Verify dims match before write-heavy flows
let col: serde_json::Value = client.get(format!("{url}/collections/{c}")).send().await?.json().await?;
let size = col.pointer("/result/config/params/vectors/size").and_then(|v| v.as_u64());
anyhow::ensure!(size == Some(embedder_dims as u64), "collection dims {size:?} != embedder {embedder_dims}"); Type guard
fn is_qdrant_upsert_error(e: &anyhow::Error) -> bool {
e.to_string().starts_with("Qdrant upsert failed")
} Try / catch
// store() generates a fresh UUID per call, so dedupe instead of blind retry
match memory.store(k, c).await {
Err(e) if is_qdrant_upsert_error(&e) => { memory.forget(k).await.ok(); memory.store(k, c).await }
other => other,
} Prevention
- One embedding model per collection; migrate to a new collection when switching
- Cap stored content size to avoid 413s
- Export before model switches so re-embedding is possible
- Make retries idempotent (dedupe by key) since point ids are per-call UUIDs
When it happens
Trigger: 400 'vector dimension mismatch' (collection built for a different embedding model); 404 collection dropped after init; 413 oversized payload; 5xx under memory pressure or OOM during indexing.
Common situations: Changed embedding provider (or model version) without recreating the collection — the classic cause; extremely large memory contents exceeding Qdrant limits; Qdrant OOM while indexing a burst of writes.
Related errors
- Qdrant search failed ({status}): {text}
- Qdrant collection creation failed ({status}): {text}
- Qdrant delete failed ({status}): {text}
- Qdrant set payload failed during agent rename ({status}): {t
- Qdrant collection info failed ({status}): {text}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/e856f8de175ca1b9.
Report an issue: GitHub.