zeroclaw-labs/zeroclaw · error
cannot rename agent memory to `{to}`: an existing memory sto
Error message
cannot rename agent memory to `{to}`: an existing memory store under that alias has {to_rows} row(s); refusing to merge What it means
rename_agent() repoints the agents.alias row, and alias is UNIQUE; the code refuses to silently merge two memory stores. It first counts memories owned by the target alias: more than zero rows raises this error with the count. An orphan target agents row owning no memories is deleted automatically, so only genuine data collisions fail.
Source
Thrown at crates/zeroclaw-memory/src/sqlite.rs:1790
// UUID); only the human `alias` column moves, so this is a single
// agents-row update. An unknown `from` matches nothing → Ok(0).
//
// Collision-safety: `agents.alias` is UNIQUE, and deleting an agent
// purges its memories but leaves the `agents` row behind (an orphan
// holding the alias). A bare UPDATE onto a previously-used-then-
// deleted `to` alias would hit the UNIQUE constraint and fail. We
// hold the connection lock across the whole sequence (single writer),
// so: refuse if `to` still has memory rows (a genuine conflict we
// won't silently merge), otherwise drop the orphan `to` row and
// proceed. (`COUNT(*)` over a NULL subselect when no `to` row exists
// is 0, so the common no-collision path falls straight through.)
let to_rows: i64 = conn.query_row(
"SELECT COUNT(*) FROM memories WHERE agent_id = (SELECT id FROM agents WHERE alias = ?1 LIMIT 1)",
params![to],
|row| row.get(0),
)?;
if to_rows > 0 {
anyhow::bail!(
"cannot rename agent memory to `{to}`: an existing memory store under that alias has {to_rows} row(s); refusing to merge"
);
}
// Drop any orphan `to` agents row (verified above to own no memories).
conn.execute("DELETE FROM agents WHERE alias = ?1", params![to])?;
let affected = conn.execute(
"UPDATE agents SET alias = ?2 WHERE alias = ?1",
params![from, to],
)?;
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
Ok(affected)
})
.await?
}
async fn count_agent(&self, agent_alias: &str) -> anyhow::Result<usize> {
let conn = self.conn.clone();
let agent_alias = agent_alias.to_string();View on GitHub (pinned to 88bb9c8533)
Solutions
- Pick a different, unused alias for the rename
- If the target store is disposable: purge it first with purge_agent(target) — its rows are removed, leaving only an orphan agents row that the rename cleans up — then retry
- If both stores must survive: export the target's memories (export_agent) before purging, or merge contents under one alias manually
- Pre-check occupancy with count_agent(target) before attempting the rename
Example fix
// before
memory.rename_agent("old", "taken").await?; // Err: 42 row(s); refusing to merge
// after
if memory.count_agent("taken").await? > 0 {
let archive = memory.export_agent("taken").await?; // keep if needed
let _ = archive;
memory.purge_agent("taken").await?; // only if disposable
}
memory.rename_agent("old", "taken").await?; Defensive patterns
Strategy: validation
Validate before calling
// Pre-check target occupancy before rename (count_agent is on the Memory trait)
if memory.count_agent(to).await? > 0 {
anyhow::bail!("alias '{to}' already owns memories; choose another alias or purge it first");
} Type guard
fn is_rename_merge_refused(e: &anyhow::Error) -> bool {
e.to_string().contains("refusing to merge")
} Try / catch
match memory.rename_agent(from, to).await {
Err(e) if is_rename_merge_refused(&e) => ask_user_for_disposition(from, to), // purge target, pick new alias, or export+merge
other => other,
} Prevention
- Check count_agent(target) before offering a rename in the UI
- Never script renames onto unknown aliases without an occupancy check
- Export the target's memories (export_agent) before purging in a rename flow
- Prefer fresh aliases over recycling recently-used ones
When it happens
Trigger: Renaming agent A to alias B while B (or a historic B that still owns memory rows) already exists in brain.db.
Common situations: Consolidating two agents by renaming one onto the other; re-using a previously active agent's name; scripted renames after re-importing an old workspace.
Related errors
- purge_agent not supported by this memory backend
- rename_agent not supported by this memory backend
- knowledge graph node limit reached ({}/{})
- Qdrant set payload failed during agent rename ({status}): {t
- SQLite connection open timed out after {} seconds
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/9dd2bd04da1825d6.
Report an issue: GitHub.