zeroclaw-labs/zeroclaw · error
Qdrant search failed
Error message
Qdrant search failed ({status}): {text} What it means
recall() embeds the query and POSTs /collections/{c}/points/search; a non-2xx response raises this error with the status and body. The search body carries the query vector, so Qdrant validates its dimension against the collection schema on every call.
Solutions
- Compare dimensions: GET /collections/{c} -> result.config.params.vectors.size vs the provider's dims; if they differ, export memories, recreate the collection under a new name (restart so ensure_collection rebuilds it) and re-embed
- Keep one collection per embedding model, or move to a new collection name when changing models
- 401/403: fix the api-key; 5xx: retry once Qdrant is ready
- If dimensions match and it still 400s, read the Qdrant body text in the message for the exact rejected field
Example fix
# before [memory] model_provider = "openai" # 1536 dims; collection was built for a 384-dim model # after: give the new model its own collection and restart so ensure_collection rebuilds it [memory.qdrant] collection = "zeroclaw-memory-openai"
Defensive patterns
Strategy: try-catch
Validate before calling
// Compare embedder dims to the collection schema before recall
let info: serde_json::Value = client.get(format!("{url}/collections/{c}")).send().await?.json().await?;
let col_dims = info.pointer("/result/config/params/vectors/size").and_then(|v| v.as_u64());
if col_dims != Some(embedder_dims as u64) { /* recreate/migrate collection before recalling */ } Type guard
fn is_qdrant_search_error(e: &anyhow::Error) -> bool {
e.to_string().starts_with("Qdrant search failed")
} Try / catch
match memory.recall(q, limit).await {
Ok(entries) => entries,
Err(e) if is_qdrant_search_error(&e) => vec![], // degrade to no memories; page the operator
Err(e) => return Err(e),
} Prevention
- Pin one embedding model per Qdrant collection; use a new collection name when switching
- Export memories before changing embedding providers
- Compare collection vector size against embedder dimensions during startup checks
When it happens
Trigger: 400 'vector dimension mismatch' after the embedding provider changed (collection was created with the old model's dimensions); 404 collection missing; 401/403 key issues; 5xx overload.
Common situations: Switching the memory model_provider (e.g., 1536-dim OpenAI vs 384-dim local model) without recreating the Qdrant collection, after which every recall fails; Qdrant upgrades tightening search payload validation.
Related errors
- Qdrant upsert failed
- Qdrant collection creation failed
- Qdrant collection info failed
- Qdrant delete failed
- Qdrant set payload failed during agent rename
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/69ca845a9008d2a9.
Report an issue: GitHub.
Appendix: source
Thrown at crates/zeroclaw-memory/src/qdrant.rs:645
if let Some(f) = filter {
search_body["filter"] = f;
}
let resp = self
.request(
reqwest::Method::POST,
&format!("/collections/{}/points/search", self.collection),
)
.json(&search_body)
.send()
.await
.context("failed to search Qdrant")?;
if !resp.status().is_success() {
let status = resp.status();
let text = resp.text().await.unwrap_or_default();
anyhow::bail!("Qdrant search failed ({status}): {text}");
}
let result: QdrantSearchResult = resp.json().await?;
let mut entries: Vec<MemoryEntry> = result
.result
.into_iter()
.filter_map(|point| {
let payload = point.payload?;
let id = match &point.id {
serde_json::Value::String(s) => s.clone(),
serde_json::Value::Number(n) => n.to_string(),
_ => return None,
};
Some(MemoryEntry {
id,
key: payload.key,View on GitHub (pinned to 88bb9c8533)