astrid-runtime/astrid · error

legacy gateway revocation path is not a regular file

Error message

legacy gateway revocation path is not a regular file: {}

What it means

Fail-closed check in the legacy revocation-file path helper: the path at etc/gateway-revocations.json exists but is a symlink or not a regular file, so the one-time migration source cannot be trusted and startup refuses to read it.

Solutions

  1. Replace the symlink/irregular path with a real regular JSON file, or delete it if migration already happened
  2. Check what created the symlink (provisioning script) and fix it
  3. Ensure the migration receipt exists in the control KV so the legacy file is no longer needed
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at crates/astrid-gateway/src/revocations.rs:62 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/7cd12d30bb84e3b2. Report an issue: GitHub.

Appendix: source

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

const MIGRATION_RECEIPT_KEY: &str = "migration/legacy-json-v1";
const MAX_REVOCATION_ENTRIES: usize = 1_000_000;

/// Released JSON file under `etc/`, retained only as a one-time migration
/// source. Runtime authority is the system control KV namespace above.
fn revocations_path() -> anyhow::Result<PathBuf> {
    let home = astrid_core::dirs::AstridHome::resolve()
        .map_err(|e| anyhow::anyhow!("resolve $ASTRID_HOME for revocation file: {e}"))?;
    Ok(home.etc_dir().join("gateway-revocations.json"))
}

/// Whether the released JSON index exists. Used only to fail closed when a
/// standalone gateway has no authoritative KV wiring during startup.
pub fn legacy_file_exists() -> anyhow::Result<bool> {
    let path = revocations_path()?;
    match std::fs::symlink_metadata(&path) {
        Ok(metadata) => {
            if metadata.file_type().is_symlink() || !metadata.is_file() {
                anyhow::bail!(
                    "legacy gateway revocation path is not a regular file: {}",
                    path.display()
                );
            }
            Ok(true)
        },
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false),
        Err(error) => Err(anyhow::anyhow!("inspect legacy revocation file: {error}")),
    }
}

/// Hard cap on the legacy migration file. Each entry is ~50 bytes of JSON;
/// `10 MiB` gives migration ample room without permitting an unbounded boot
/// allocation from a corrupted or hostile operator file.
const MAX_REVOCATIONS_FILE_BYTES: u64 = 10 * 1024 * 1024;

fn read_legacy_bytes(path: &std::path::Path) -> anyhow::Result<Vec<u8>> {
    #[cfg(unix)]

View on GitHub (pinned to affd8760f4)