zeroclaw-labs/zeroclaw · error

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

Error message

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

What it means

delete_points_matching POSTs /collections/{c}/points/delete with a filter; it implements forget(), forget_for_agent(), purge_session_for_agent(), purge_agent(), and the supersede step of store_with_agent(). This error means Qdrant refused the delete with a non-2xx status.

Source

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

    async fn delete_points_matching(&self, fields: &[(&str, &str)]) -> Result<bool> {
        self.ensure_initialized().await?;

        let delete_body = serde_json::json!({"filter": Self::must_filter(fields)});
        let resp = self
            .request(
                reqwest::Method::POST,
                &format!("/collections/{}/points/delete", self.collection),
            )
            .query(&[("wait", "true")])
            .json(&delete_body)
            .send()
            .await
            .context("failed to delete from Qdrant")?;

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

        Ok(true)
    }
}

/// Qdrant point payload structure
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MemoryPayload {
    key: String,
    content: String,
    category: String,
    timestamp: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    session_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    agent_id: Option<String>,
}

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Check the status in the message; 404 means recreate the collection and restart the process so init re-runs
  2. 5xx/503: retry the forget after load drops or Qdrant reports ready — deletes are idempotent
  3. 400: inspect key/session_id values for characters that break the filter JSON
  4. Repeated lock timeouts: review Qdrant optimizer and memory settings
Defensive patterns

Strategy: retry

Type guard

fn is_qdrant_delete_error(e: &anyhow::Error) -> bool {
    e.to_string().starts_with("Qdrant delete failed")
}

Try / catch

for attempt in 0..3 {
    match memory.forget(key).await {
        Ok(n) => break,
        Err(e) if is_qdrant_delete_error(&e) && attempt < 2 => { tokio::time::sleep(backoff(attempt)).await; }
        Err(e) => return Err(e),
    }
} // deletes are idempotent; a retry cannot double-delete

Prevention

When it happens

Trigger: 404 collection missing (dropped after init); 400 malformed filter; 409/write-lock timeout under heavy upsert load; 5xx during Qdrant maintenance or optimization.

Common situations: Forgetting memories while the collection was rebuilt; deleting during heavy write traffic; Qdrant under memory pressure returning 503.

Related errors


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