zeroclaw-labs/zeroclaw · error

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

Error message

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

What it means

A paginated Qdrant scroll (POST /collections/{name}/points/scroll with an agent-id filter) returned a non-success HTTP status; the status code and response body are surfaced verbatim. This scroll backs purge_agent, export_agent, rename_agent, count_agent, and recall_for_agents, so any of those agent-scoped operations can surface it.

Source

Thrown at crates/zeroclaw-memory/src/qdrant.rs:173

            "limit": 1000,
            "with_payload": true,
            "filter": { "must": must_conditions }
        });

        let resp = self
            .request(
                reqwest::Method::POST,
                &format!("/collections/{}/points/scroll", self.collection),
            )
            .json(&scroll_body)
            .send()
            .await
            .context("failed to scroll Qdrant for allowed agent set")?;

        if !resp.status().is_success() {
            let status = resp.status();
            let text = resp.text().await.unwrap_or_default();
            anyhow::bail!("Qdrant scroll failed ({status}): {text}");
        }

        let result: QdrantScrollResult = resp.json().await?;

        let entries = result
            .result
            .points
            .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,

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the status and body: 401/403 means credentials in [storage.qdrant.<alias>], 404 means the collection is gone, 400 usually means collection-name or filter shape, 5xx is server-side.
  2. Verify url, api_key, and collection still match the live cluster.
  3. Retry transient 5xx after a short backoff; re-create the collection (ensure_collection runs on init) if it was deleted.

Example fix

# diagnose against the same cluster
$ curl -H "api-key: $QDRANT_KEY" \
    http://qdrant-host:6333/collections/zeroclaw_memories/points/scroll \
    -X POST -H 'Content-Type: application/json' \
    -d '{"limit": 1, "with_payload": true}'
Defensive patterns

Strategy: try-catch

Try / catch

match memory.recall_for_agents(query, limit, &agents).await {
    Ok(v) => Ok(v),
    Err(e) => {
        let msg = e.to_string();
        if msg.contains("Qdrant scroll failed (401") || msg.contains("Qdrant scroll failed (403") {
            return Err(e.context("qdrant credentials rejected; fix [storage.qdrant.<alias>].api_key"));
        }
        if msg.contains("Qdrant scroll failed (404") {
            return Err(e.context("qdrant collection missing; recreate it via ensure_initialized"));
        }
        if msg.contains("Qdrant scroll failed (5") {
            // server-side: safe to retry with backoff
        }
        Err(e)
    }
}

Prevention

When it happens

Trigger: Qdrant answers 401/403 (bad or rotated API key), 404 (collection dropped mid-operation), 400 (malformed filter or invalid collection name), or 5xx while the scroll executes. Transport-level failures surface as a different context error; this one means Qdrant answered with an error status.

Common situations: Expired Qdrant API key; collection recreated without the expected payload; Qdrant restarts or cloud maintenance windows during long scrolls; the collection deleted by another operator between check and scroll.

Related errors


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