zeroclaw-labs/zeroclaw · error

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

Error message

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

What it means

After ensuring the collection, QdrantMemory::migrate_session_ids_to_sanitized pages through every point (POST /collections/{c}/points/scroll, limit 1000, following next_page_offset) to find session_id payloads that need rewriting. This error means one of those scroll pages returned non-2xx; the migration runs inside new()/ensure_initialized() on every startup while the embedder reports non-zero dimensions.

Source

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

            });
            if let Some(ref offset) = next_offset {
                scroll_body["offset"] = offset.clone();
            }

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

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

            let page: QdrantScrollResult = resp.json().await?;
            for point in &page.result.points {
                if let Some(ref payload) = point.payload
                    && let Some(ref sid) = payload.session_id
                {
                    seen.insert(sid.clone());
                }
            }

            match page.result.next_page_offset {
                Some(offset) if !offset.is_null() => next_offset = Some(offset),
                _ => break,
            }
        }

        let mut rewritten = 0usize;

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the status in the message: 429 means back off and retry — lazy init re-runs the migration on the next operation after a restart
  2. 404 means the collection vanished; recreate it (restart the process so ensure_collection runs) and restore from backup if needed
  3. 5xx/504 means Qdrant is unhealthy or the proxy timed out: check GET /readyz and raise proxy read timeouts for scroll endpoints
  4. Retry startup; the migration is idempotent and rescans from scratch each run
Defensive patterns

Strategy: retry

Validate before calling

// Gate startup on Qdrant health and collection presence before first memory op
let ready = client.get(format!("{url}/readyz")).send().await?.status().is_success();
let exists = client.get(format!("{url}/collections/{collection}")).send().await?.status().is_success();

Type guard

fn is_migration_scroll_error(e: &anyhow::Error) -> bool {
    e.to_string().starts_with("Qdrant scroll failed during migration")
}

Try / catch

if let Err(e) = memory.get("warmup").await {
    if is_migration_scroll_error(&e) { /* back off, restart op; init retries lazily */ }
}

Prevention

When it happens

Trigger: Collection deleted or renamed out-of-band between creation and the scroll; 429 rate limiting on Qdrant Cloud while scrolling a large collection; 400 from a stale/invalid offset after a Qdrant version change; 502/504 from a reverse proxy timing out on a large page.

Common situations: Qdrant Cloud free tier with a large memory collection whose scroll bursts trip rate limits; someone dropping/recreating collections while agents restart; Nginx/Traefik in front of Qdrant with a low proxy_read_timeout on /points/scroll.

Related errors


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