zeroclaw-labs/zeroclaw · error

Qdrant search failed ({status}): {text}

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.

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)

Solutions

  1. 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
  2. Keep one collection per embedding model, or move to a new collection name when changing models
  3. 401/403: fix the api-key; 5xx: retry once Qdrant is ready
  4. 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

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


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/69ca845a9008d2a9. Report an issue: GitHub.