affaan-m/ECC · error · anyhow::Error

candidate alias collision with physical candidate id

Error message

candidate alias collision with physical candidate id

What it means

Thrown by register_harness_alias when the proposed alias_id is already present as a physical candidate id in the harness_candidates table. The store forbids an alias from shadowing a real candidate id so the id namespace stays unambiguous: a lookup of alias_id must not silently resolve to two different things.

Source

Thrown at ecc2/src/session/store.rs:5307

    }
}

impl StateStore {
    fn register_harness_alias(
        tx: &rusqlite::Transaction<'_>,
        alias_id: &str,
        candidate_id: &str,
    ) -> Result<()> {
        if tx
            .query_row(
                "SELECT 1 FROM harness_candidates WHERE id = ?1",
                [alias_id],
                |_| Ok(()),
            )
            .optional()?
            .is_some()
        {
            anyhow::bail!("candidate alias collision with physical candidate id");
        }
        let existing = tx
            .query_row(
                "SELECT candidate_id FROM harness_candidate_aliases WHERE alias_id = ?1",
                [alias_id],
                |row| row.get::<_, String>(0),
            )
            .optional()?;
        if let Some(existing) = existing {
            if existing != candidate_id {
                anyhow::bail!("candidate alias collision with different immutable target");
            }
            return Ok(());
        }
        tx.execute(
            "INSERT INTO harness_candidate_aliases (alias_id, candidate_id, id_version, created_at) VALUES (?1, ?2, 2, ?3)",
            rusqlite::params![alias_id, candidate_id, chrono::Utc::now().to_rfc3339()],
        )?;

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Pick an alias_id that is provably not already a physical candidate id (query harness_candidates first).
  2. If the alias_id must equal that value, drop or rename the physical candidate row before registering the alias.
  3. Namespace aliases distinctly from physical ids (e.g. a prefix) so the two sets cannot overlap by construction.

Example fix

// before
store.register_harness_alias(&tx, &legacy_id, &candidate.id)?;

// after
let taken: Option<i64> = tx.query_row(
    "SELECT 1 FROM harness_candidates WHERE id = ?1",
    [&legacy_id], |_| Ok(1)).optional()?;
if taken.is_some() {
    anyhow::bail!("refusing to alias legacy_id {legacy_id}: it is a physical candidate id");
}
store.register_harness_alias(&tx, &legacy_id, &candidate.id)?;
Defensive patterns

Strategy: validation

Validate before calling

fn alias_is_physical_candidate(tx: &rusqlite::Transaction, alias_id: &str) -> Result<bool> {
    let exists: Option<i64> = tx.query_row(
        "SELECT 1 FROM harness_candidates WHERE id = ?1",
        [alias_id], |_| Ok(1)).optional()?;
    Ok(exists.is_some())
}

// before register_harness_alias:
if alias_is_physical_candidate(&tx, alias_id)? {
    return Err(anyhow::anyhow!("alias_id {alias_id} is a physical candidate id; choose a distinct alias"));
}

Type guard

fn safe_alias_id(tx: &rusqlite::Transaction, alias_id: &str) -> bool {
    !tx.query_row("SELECT 1 FROM harness_candidates WHERE id = ?1", [alias_id], |_| Ok(1))
        .optional().ok().flatten().is_some()
}

Try / catch

match store.register_harness_alias(&tx, alias_id, candidate_id) {
    Ok(()) => { /* registered */ }
    Err(e) if e.to_string().contains("alias collision with physical candidate id") => {
        // choose a different alias_id and retry, or surface to caller
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling register_harness_alias(tx, alias_id, candidate_id) where SELECT 1 FROM harness_candidates WHERE id = alias_id returns a row. Typical when a legacy id being registered as an alias happens to equal an already-stored v2 content-addressed candidate id.

Common situations: Migrating a legacy id scheme where a legacy id was itself promoted to a v2 candidate id; reusing one 64-char hex value as both a physical candidate and an alias target; two subsystems deriving the same digest for different purposes.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/dc48d457f7ad7e7b. Report an issue: GitHub.