zeroclaw-labs/zeroclaw · error · anyhow::Error
Unable to allocate non-conflicting key for '{base}'
Error message
Unable to allocate non-conflicting key for '{base}' What it means
During migration, when an imported key collides with an existing ZeroClaw memory, next_available_key searches for a free name of the form {base}__openclaw_{i}, probing successive i values against the live memory store. If every candidate in the bounded probe range is taken, it bails. Under normal operation this is near-impossible: it requires the entire suffix namespace for a base key to be pre-filled.
Source
Thrown at crates/zeroclaw-runtime/src/migration.rs:349
}
fn normalize_key(key: &str, fallback_idx: usize) -> String {
let trimmed = key.trim();
if trimmed.is_empty() {
return format!("openclaw_{fallback_idx}");
}
trimmed.to_string()
}
async fn next_available_key(memory: &dyn Memory, base: &str) -> Result<String> {
for i in 1..=10_000 {
let candidate = format!("{base}__openclaw_{i}");
if memory.get(&candidate).await?.is_none() {
return Ok(candidate);
}
}
bail!("Unable to allocate non-conflicting key for '{base}'")
}
fn table_columns(conn: &Connection, table: &str) -> Result<Vec<String>> {
let pragma = format!("PRAGMA table_info({table})");
let mut stmt = conn.prepare(&pragma)?;
let rows = stmt.query_map([], |row| row.get::<_, String>(1))?;
let mut cols = Vec::new();
for col in rows {
cols.push(col?.to_ascii_lowercase());
}
Ok(cols)
}
fn pick_optional_column_expr(columns: &[String], candidates: &[&str]) -> Option<String> {
candidates
.iter()View on GitHub (pinned to 88bb9c8533)
Solutions
- List memories matching the base key and delete or rename the stale __openclaw_ suffixed entries, then re-run
- If the target store is disposable, migrate into a fresh workspace instead
- If a single normal run triggers this, treat it as a bug — file it with the base key and memory count
Defensive patterns
Strategy: validation
Validate before calling
for i in 0..LIMIT {
let candidate = format!("{base}__openclaw_{i}");
if !memory_key_exists(&memory, &candidate).await? {
return Ok(candidate); // safe to proceed with migration
}
}
anyhow::bail!("suffix space exhausted for {base}; clean stale __openclaw_ keys"); Try / catch
Err(e) if e.to_string().contains("non-conflicting key") => {
// list and remove stale {base}__openclaw_* memories from interrupted runs, then re-run once
} Prevention
- Make migrations idempotent: record imported keys or clean __openclaw_ suffixed entries on restart
- Alert when a workspace accumulates many __openclaw_ suffixed keys — sign of repeated partial runs
- Prefer migrating into a fresh target workspace when retrying after failures
When it happens
Trigger: Repeated interrupted migrations that seeded base__openclaw_0..N keys without completing, then re-running migration; a workspace where users (or another tool) deliberately created keys shaped base__openclaw_<i>; adversarial/pathological key sets imported earlier.
Common situations: Migration retried many times after partial failures; scripted loops that rerun migrate on conflict-heavy workspaces; prior imports from another OpenClaw workspace into the same ZeroClaw store.
Related errors
- AgentScopedMemory refuses purge_namespace: cross-agent bulk
- memory consolidation write denied by policy: {e}
- memory fact write denied by policy: {e}
- Embedding API error {status}: {text}
- OpenClaw workspace not found at {}. Pass --source <path> if
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/b3c93ee746685060.
Report an issue: GitHub.