zeroclaw-labs/zeroclaw · error

memory backend 'qdrant' requires a `[storage.qdrant.<alias>]

Error message

memory backend 'qdrant' requires a `[storage.qdrant.<alias>]` entry referenced by `memory.backend = "qdrant.<alias>"`

What it means

The backend classified as qdrant, but the storage resolved from the config is not an ActiveStorage::Qdrant entry — the [storage.qdrant.<alias>] section referenced by memory.backend = "qdrant.<alias>" is missing or belongs to another storage family. The qdrant client needs that section's url (required, non-empty), collection, and api_key, so the factory bails with the exact TOML shape expected.

Source

Thrown at crates/zeroclaw-memory/src/lib.rs:681

                    dimensions: resolved_embedding.dimensions,
                },
                config.auto_reindex_on_identity_change,
            );
        }
        Ok(mem)
    }

    // Per-backend SQLite open-timeout override comes from the active storage
    // alias (V3); when no typed entry resolves, sqlite waits indefinitely.
    let sqlite_open_timeout_secs = match active_storage {
        ActiveStorage::Sqlite(sq) => sq.open_timeout_secs,
        _ => None,
    };

    if matches!(backend_kind, MemoryBackendKind::Qdrant) {
        let qdrant_cfg = match active_storage {
            ActiveStorage::Qdrant(q) => q,
            _ => anyhow::bail!(
                "memory backend 'qdrant' requires a `[storage.qdrant.<alias>]` entry \
                 referenced by `memory.backend = \"qdrant.<alias>\"`"
            ),
        };
        let url = qdrant_cfg
            .url
            .clone()
            .filter(|s| !s.trim().is_empty())
            .context("Qdrant memory backend requires `url` in [storage.qdrant.<alias>]")?;
        let collection = qdrant_cfg.collection.clone();
        let qdrant_api_key = qdrant_cfg.api_key.clone().filter(|s| !s.trim().is_empty());
        let embedder: Arc<dyn embeddings::EmbeddingProvider> =
            Arc::from(embeddings::create_embedding_provider(
                &resolved_embedding.model_provider,
                resolved_embedding.api_key.as_deref(),
                &resolved_embedding.model,
                resolved_embedding.dimensions,
            ));

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Add the matching section: `[storage.qdrant.<alias>]` with `url = "http://host:6333"` (plus optional collection and api_key), and keep `memory.backend = "qdrant.<alias>"` aligned.
  2. Fix the alias typo so memory.backend's suffix exactly matches the TOML section name.
  3. Check nesting: the alias is one level below the family, i.e. [storage.qdrant.prod], not [storage.qdrant] prod = ... in the wrong shape.

Example fix

# before
[memory]
backend = "qdrant.prod"

# after
[memory]
backend = "qdrant.prod"

[storage.qdrant.prod]
url = "http://localhost:6333"
collection = "zeroclaw_memories"
Defensive patterns

Strategy: validation

Validate before calling

let backend = backend_kind_from_dotted(&config.memory.backend);
if backend == "qdrant"
    && !matches!(config.resolve_active_storage(), ActiveStorage::Qdrant(_))
{
    anyhow::bail!(
        "memory.backend = {:?} has no matching [storage.qdrant.<alias>] section",
        config.memory.backend
    );
}

Prevention

When it happens

Trigger: `memory.backend = "qdrant.prod"` while `[storage.qdrant.prod]` does not exist: alias typo (prodd vs prod), the section written without the alias level ([storage.qdrant] instead of [storage.qdrant.prod]), or the alias defined only under a different family ([storage.postgres.prod]). Also fires for a bare backend = "qdrant" with no alias entry at all.

Common situations: Renaming storage aliases without updating memory.backend; environment overlay files that drop the [storage.*] section; merging config layers where storage and memory come from different sources.

Related errors


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