astrid-runtime/astrid · error

gateway revocation migration receipt conflicts

Error message

gateway revocation migration receipt conflicts

What it means

Receipt conflict in migrate_legacy_file: a migration receipt already exists in the control KV but its encoded bytes differ from the receipt computed for this legacy file — a different digest or count, meaning the file changed after a previous migration or two sources disagree.

Solutions

  1. Compare the existing receipt's digest with the current file's blake3 digest
  2. If the file was edited after migration, restore the original or re-derive entries manually
  3. If the existing receipt is stale/incorrect, remove it deliberately and re-run migration
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/astrid-gateway/src/revocations.rs:417 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/4d6d526344a6b543. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-gateway/src/revocations.rs:417

        if durable < *epoch {
            anyhow::bail!(
                "principal revocation {principal} read back epoch {durable}, expected at least {epoch}"
            );
        }
    }
    let receipt = LegacyMigrationReceipt {
        schema: 1,
        digest,
        principal_count: entries.len(),
    };
    let encoded = serde_json::to_vec(&receipt).context("encode revocation migration receipt")?;
    let existing = store
        .get(REVOCATION_NAMESPACE, MIGRATION_RECEIPT_KEY)
        .await
        .map_err(|error| anyhow::anyhow!("read revocation migration receipt: {error}"))?;
    if let Some(existing) = existing {
        if existing != encoded {
            anyhow::bail!("gateway revocation migration receipt conflicts");
        }
    } else if !store
        .compare_and_swap(REVOCATION_NAMESPACE, MIGRATION_RECEIPT_KEY, None, encoded)
        .await
        .map_err(|error| anyhow::anyhow!("write revocation migration receipt: {error}"))?
    {
        anyhow::bail!("gateway revocation migration receipt raced; retry startup");
    }
    let _ = load_from_store(store).await?;
    match std::fs::remove_file(&path) {
        Ok(()) => {},
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {},
        Err(error) => return Err(anyhow::anyhow!("retire {}: {error}", path.display())),
    }
    Ok(true)
}

/// Spawn the audit-event watcher. Subscribes to the kernel's audit

View on GitHub (pinned to affd8760f4)