AprilNEA/OpenLogi · error

must be a non-symlink regular file

Error message

{asset} must be a non-symlink regular file

What it means

`require_regular_file` demands that an asset path be a plain regular file and not a symlink, using `symlink_metadata` so a symlink is rejected even if it targets a regular file. Fixture integrity checks refuse linked files because their content could silently change with the link target.

Solutions

  1. Replace the symlink with a real copy: `rm <asset> && cp <target> <asset>`.
  2. Regenerate the fixture so the file is written in place rather than linked.
  3. Check before running: `[ -f <asset> ] && [ ! -L <asset> ]`.

Example fix

// before
ln -s ../../common/manifest.json out/mx-master/manifest.json
// after
cp ../../common/manifest.json out/mx-master/manifest.json
Defensive patterns

Strategy: validation

Validate before calling

[ -f "$ASSET" ] && [ ! -L "$ASSET" ] || { echo "$ASSET must be a regular non-symlink file" >&2; exit 1; }

Prevention

When it happens

Trigger: finish, validate_resumable_layout, or validate_resumable_cases inspects an asset file that is a symlink, or is not a regular file (a directory or missing path — missing paths surface via the `could not inspect {asset}` context instead).

Common situations: Dotfile-style setups that symlink fixture JSON into a shared repo; generating fixture files as symlinks to templates; case files hard-linked/symlinked between fixtures.

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 AprilNEA/OpenLogi@e846e6f4b4 (2026-09-13). Data as JSON: /api/errors/32b0987373fbab35. Report an issue: GitHub.

Appendix: source

Thrown at crates/openlogi-cli/src/cmd/fixture/contribute.rs:495

fn read_json<T: serde::de::DeserializeOwned>(path: &Path, asset: &str) -> Result<T> {
    let bytes = fs::read(path).with_context(|| format!("could not read {asset}"))?;
    serde_json::from_slice(&bytes).with_context(|| format!("could not parse {asset}"))
}

fn require_directory(path: &Path, asset: &str) -> Result<()> {
    let metadata =
        fs::symlink_metadata(path).with_context(|| format!("could not inspect {asset}"))?;
    if metadata.file_type().is_symlink() || !metadata.is_dir() {
        bail!("{asset} must be a non-symlink directory");
    }
    Ok(())
}

fn require_regular_file(path: &Path, asset: &str) -> Result<()> {
    let metadata =
        fs::symlink_metadata(path).with_context(|| format!("could not inspect {asset}"))?;
    if metadata.file_type().is_symlink() || !metadata.is_file() {
        bail!("{asset} must be a non-symlink regular file");
    }
    Ok(())
}

fn reject_symlink(path: &Path, asset: &str) -> Result<()> {
    match fs::symlink_metadata(path) {
        Ok(metadata) if metadata.file_type().is_symlink() => {
            bail!("{asset} output must not be a symlink")
        }
        Ok(_) => Ok(()),
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
        Err(error) => Err(error).with_context(|| format!("could not inspect {asset} output")),
    }
}

#[cfg(test)]
mod tests {
    use openlogi_fixture::CANONICAL_DEVICE_PROFILE_JSON;

View on GitHub (pinned to e846e6f4b4)