astrid-runtime/astrid · error · anyhow::Error
inspect legacy revocation file: {error}
Error message
inspect legacy revocation file: {error} What it means
legacy_file_exists stats the legacy revocation JSON file to decide whether a one-time migration is needed. NotFound maps to Ok(false) (no legacy file — normal), but any other I/O error opening/inspecting the file (permission denied, is-a-directory, etc.) is wrapped in this error. It means the legacy file exists but could not be inspected.
Source
Thrown at crates/astrid-gateway/src/revocations.rs:70
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)]
let file = {
use std::os::unix::fs::OpenOptionsExt as _;
std::fs::OpenOptions::new()
.read(true)
.custom_flags(nix::libc::O_NOFOLLOW | nix::libc::O_CLOEXEC)
.open(path)
.with_context(|| format!("open legacy revocation file {}", path.display()))?
};View on GitHub (pinned to affd8760f4)
Solutions
- Check permissions on $ASTRID_HOME/etc and the gateway-revocations.json path so the service user can read it
- Verify the path is a regular file, not a directory or broken symlink (ls -la)
- If the legacy file is no longer needed, remove/rename it so the stat resolves to NotFound
- Inspect the wrapped std::io::Error for the exact OS errno
Example fix
# before ls -la $ASTRID_HOME/etc/gateway-revocations.json # permission denied / is a directory # after sudo chown astrid:astrid $ASTRID_HOME/etc/gateway-revocations.json # or remove the stray directory and restore the file / delete it if migration is done
Defensive patterns
Strategy: try-catch
Validate before calling
fn legacy_path_sane(home: &std::path::Path) -> bool {
let p = home.join("etc").join("gateway-revocations.json");
match std::fs::metadata(&p) {
Ok(m) => m.is_file(),
Err(e) => e.kind() == std::io::ErrorKind::NotFound,
}
} Try / catch
match revocations::legacy_file_exists() {
Ok(exists) => exists,
Err(e) if e.to_string().contains("inspect legacy revocation file") => {
log::warn!("legacy revocation file unreadable, skipping migration: {e}");
false // or fail closed per startup policy
}
Err(e) => return Err(e),
} Prevention
- Ensure the service user owns/read access to $ASTRID_HOME/etc
- Never replace the JSON file with a directory or symlink loop
- Clean up the legacy file once migration is complete
When it happens
Trigger: std::fs metadata/open of home.etc_dir()/gateway-revocations.json fails with an error kind other than NotFound — e.g. EACCES (permission denied), EISDIR (path is a directory), or ELOOP.
Common situations: gateway-revocations.json accidentally replaced by a directory; service user lacking read permission on etc/; filesystem errors (broken symlink loop, NFS issues) on the host.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- private host-file permissions are unavailable on this target
- private directory is not owned by the current user: {}
- private directory is not owner-only: {}
- scan {}: {error}
- capsules are installed, but granting capsule access failed:
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/a555a8e6ab8e8734.
Report an issue: GitHub.