{"record":{"id":"c2e5459d97dcc567","repo":"zeroclaw-labs/zeroclaw","slug":"qdrant-scroll-failed-during-migration-status","errorCode":null,"errorMessage":"Qdrant scroll failed during migration ({status}): {text}","messagePattern":"Qdrant scroll failed during migration \\((.+?)\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-memory/src/qdrant.rs","lineNumber":316,"sourceCode":"            });\n            if let Some(ref offset) = next_offset {\n                scroll_body[\"offset\"] = offset.clone();\n            }\n\n            let resp = self\n                .request(\n                    reqwest::Method::POST,\n                    &format!(\"/collections/{}/points/scroll\", self.collection),\n                )\n                .json(&scroll_body)\n                .send()\n                .await\n                .context(\"failed to scroll Qdrant for session_id migration\")?;\n\n            if !resp.status().is_success() {\n                let status = resp.status();\n                let text = resp.text().await.unwrap_or_default();\n                anyhow::bail!(\"Qdrant scroll failed during migration ({status}): {text}\");\n            }\n\n            let page: QdrantScrollResult = resp.json().await?;\n            for point in &page.result.points {\n                if let Some(ref payload) = point.payload\n                    && let Some(ref sid) = payload.session_id\n                {\n                    seen.insert(sid.clone());\n                }\n            }\n\n            match page.result.next_page_offset {\n                Some(offset) if !offset.is_null() => next_offset = Some(offset),\n                _ => break,\n            }\n        }\n\n        let mut rewritten = 0usize;","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-memory/src/qdrant.rs#L298-L334","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","404 means the collection vanished; recreate it (restart the process so ensure_collection runs) and restore from backup if needed","5xx/504 means Qdrant is unhealthy or the proxy timed out: check GET /readyz and raise proxy read timeouts for scroll endpoints","Retry startup; the migration is idempotent and rescans from scratch each run"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Gate startup on Qdrant health and collection presence before first memory op\nlet ready = client.get(format!(\"{url}/readyz\")).send().await?.status().is_success();\nlet exists = client.get(format!(\"{url}/collections/{collection}\")).send().await?.status().is_success();","typeGuard":"fn is_migration_scroll_error(e: &anyhow::Error) -> bool {\n    e.to_string().starts_with(\"Qdrant scroll failed during migration\")\n}","tryCatchPattern":"if let Err(e) = memory.get(\"warmup\").await {\n    if is_migration_scroll_error(&e) { /* back off, restart op; init retries lazily */ }\n}","preventionTips":["Stagger multi-instance restarts so only one runs the session_id migration","Gate boot on GET /readyz","Keep reverse-proxy read timeouts generous for /points/scroll","Avoid dropping/recreating collections while agents boot"],"tags":["qdrant","scroll","pagination","migration","startup","rust"],"backgroundTag":"qdrant-api-error","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}