AprilNEA/OpenLogi · error

relationship verification failed: missing declared fixture…

Error message

relationship verification failed: missing declared fixture case file {missing:?}

What it means

After scanning the fixture directory, a case declared in the manifest has no corresponding `*.json` cassette file on disk (checked via `expected.keys().find(|name| !found.contains_key(name))`). The verifier treats every declared case as required, so a declared-but-absent cassette fails relationship verification.

Solutions

  1. Create or re-record the missing cassette file `<case-name>.json` inside the fixture directory.
  2. If the case is no longer needed, remove its entry from the fixture manifest so declarations and files agree.
  3. Check git status/history to see if the cassette was accidentally deleted or ignored, and restore it.
  4. Re-run `openlogi fixture verify`.
Defensive patterns

Strategy: validation

Validate before calling

let declared: Vec<String> = manifest_cases();
for case in &declared {
    let f = format!("{case}.json");
    assert!(fixture_dir.join(&f).is_file(), "declared case file missing: {f}");
}

Prevention

When it happens

Trigger: Running `openlogi fixture verify` when the fixture manifest lists a case whose `case_file_name(case).json` file is missing from the fixture directory — e.g. the cassette was deleted or never recorded while the manifest still declares it.

Common situations: A cassette `.json` was removed or renamed by hand or by an incomplete merge; a new case was added to the manifest before its cassette was captured; `.gitignore` or a bad git checkout dropped the file.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of AprilNEA/OpenLogi@e846e6f4b4 (2026-09-13). Data as JSON: /api/errors/8db0228451096baa. Report an issue: GitHub.

Appendix: source

Thrown at crates/openlogi-cli/src/cmd/fixture/verify.rs:165

    let mut found = BTreeMap::new();
    for entry in read_directory(directory, "fixture cases directory")? {
        require_regular_file(&entry, "fixture cassette")?;
        let file_name = entry_name(&entry)?;
        let Some(case_name) = expected.get(&file_name) else {
            bail!("relationship verification failed: undeclared fixture case file {file_name:?}");
        };
        let cassette: HidCassette = read_json(&entry.path(), "HID cassette")?;
        if cassette.name != *case_name {
            bail!(
                "relationship verification failed: cassette file {file_name:?} contains case {:?}",
                cassette.name
            );
        }
        found.insert(file_name, cassette);
    }

    if let Some(missing) = expected.keys().find(|name| !found.contains_key(*name)) {
        bail!("relationship verification failed: missing declared fixture case file {missing:?}");
    }
    Ok(found.into_values().collect())
}

fn case_file_name(case_name: &str) -> Result<String> {
    if matches!(case_name, "." | "..") || case_name.contains('/') || case_name.contains('\\') {
        bail!(
            "relationship verification failed: fixture case name {case_name:?} is not a safe file name"
        );
    }
    Ok(format!("{case_name}.json"))
}

fn require_directory_id(directory: &Path, fixture_id: &str) -> Result<()> {
    if directory.file_name().and_then(|name| name.to_str()) == Some(fixture_id) {
        Ok(())
    } else {
        bail!(

View on GitHub (pinned to e846e6f4b4)