astrid-runtime/astrid · error

leftover capsule authority receipt is not a regular file: {}

Error message

leftover capsule authority receipt is not a regular file: {}

What it means

`parse_legacy_authority_receipt` only accepts regular files as leftover authority receipts. This error is thrown when `symlink_metadata` shows the path is a symlink, directory, or other non-regular file. Receipts are security-sensitive: a symlink could redirect reads elsewhere, so the library hard-fails instead of following it (invalid JSON, by contrast, is tolerated as `Ok(None)`).

Source

Thrown at crates/astrid-capsule-install/src/authority/leftover.rs:71

        );
    }
    if !status.previous.is_empty() {
        bail!(
            "cannot retire leftover capsule authority while previous transaction artifact exists at {}",
            status.previous[0].display()
        );
    }
    Ok(status.unknown_active)
}

/// Parse a regular-file leftover receipt. Invalid JSON returns `Ok(None)`.
pub(crate) fn parse_legacy_authority_receipt(
    path: &Path,
) -> anyhow::Result<Option<(InstalledAuthority, Vec<u8>)>> {
    let metadata = fs::symlink_metadata(path)
        .with_context(|| format!("inspect leftover capsule authority {}", path.display()))?;
    if metadata.file_type().is_symlink() || !metadata.is_file() {
        bail!(
            "leftover capsule authority receipt is not a regular file: {}",
            path.display()
        );
    }
    astrid_core::platform_fs::verify_no_redirects(path).with_context(|| {
        format!(
            "verify leftover capsule authority receipt {}",
            path.display()
        )
    })?;
    let bytes = fs::read(path)
        .with_context(|| format!("read leftover capsule authority {}", path.display()))?;
    let Ok(receipt) = serde_json::from_slice::<InstalledAuthority>(&bytes) else {
        return Ok(None);
    };
    if receipt.schema_version != 1 {
        return Ok(None);
    }

View on GitHub (pinned to affd8760f4)

Solutions

  1. Replace the symlink or non-regular entry with a real regular-file receipt (copy the target's bytes into the authority directory), then retry
  2. If the entry is not a valid receipt, remove it or quarantine it manually and rerun the leftover sweep
  3. Audit the home's authority receipt directory for symlinks and ensure restore/copy tooling dereferences symlinks into plain files

Example fix

// before: authority dir contains a symlinked receipt
bail!("leftover capsule authority receipt is not a regular file: {}", path.display());
// after: replace symlink with a regular file
// let target = fs::read_link(&path)?;
// fs::remove_file(&path)?;
// fs::copy(target, &path)?;   // then retry the sweep
Defensive patterns

Strategy: validation

Validate before calling

// Inspect leftover receipt paths before parsing
let metadata = fs::symlink_metadata(&path)?;
if metadata.file_type().is_symlink() || !metadata.is_file() {
    // replace with a regular file or quarantine the entry before the sweep
}

Type guard

fn is_regular_file(path: &Path) -> bool {
    std::fs::symlink_metadata(path)
        .map(|m| !m.file_type().is_symlink() && m.is_file())
        .unwrap_or(false)
}

Try / catch

match parse_legacy_authority_receipt(&path) {
    Err(e) if e.to_string().contains("not a regular file") => quarantine_or_replace(&path)?,
    other => other,
}

Prevention

When it happens

Trigger: Calling `parse_legacy_authority_receipt` (used by `unique_relocated_receipt`, `leftover_id_counts`, `retire_one_leftover`) on a path inside the legacy authority directory that is a symlink or not a regular file — e.g. someone symlinked a receipt to another location, or a directory was created where a receipt file should be.

Common situations: User convenience symlinks placed into the authority receipt directory to share receipts across homes; backup/restore tools that replaced files with symlinks; a directory accidentally created where a `.json` receipt belongs.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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