AprilNEA/OpenLogi · error

relationship verification failed: undeclared fixture case…

Error message

relationship verification failed: undeclared fixture case file {file_name:?}

What it means

When cases are declared, `load_cassettes` reads the cases directory and requires every file present to correspond to a manifest-declared case (looked up by derived file name). Any cassette file that no manifest entry maps to triggers this bail, keeping the fixture free of orphaned cassettes. It throws because loading an undeclared cassette would pair real recorded data with the wrong (or no) case definition.

Solutions

  1. Delete the orphaned cassette file named in the error from the cases directory
  2. Or add/rename the corresponding case in the manifest so `case_file_name` matches the existing file
  3. Sync both sides: after any case rename, rename the cassette file to the newly derived name

Example fix

// before
cases/
  scroll-up.toml   // manifest has only "scroll-down"
// after
cases/
  scroll-up.toml   // manifest declares "scroll-up" (and any others present)
Defensive patterns

Strategy: validation

Validate before calling

# pre-check: every cassette file must appear (by derived name) in the manifest
for f in fixture-dir/cases/*; do
  base=$(basename "$f");
  grep -q "$(strip_ext "$base")" manifest.txt || echo "orphan cassette: $base";
done

Try / catch

match load(fixture_dir) {
    Err(e) if e.to_string().starts_with("relationship verification failed: undeclared fixture case file") => {
        let file = extract_file_name(&e); // delete it or declare its case
        resolve_orphan(fixture_dir.join(CASES_DIRECTORY).join(file));
        load(fixture_dir)
    }
    other => other,
}

Prevention

When it happens

Trigger: A `.json` cassette file exists in the cases directory but its file name does not match any case name in the manifest — e.g. a case was removed or renamed in the manifest while its cassette file remained, or a cassette was copied in from another fixture.

Common situations: Renaming a case in the manifest without renaming its cassette file (or vice versa); deleting a manifest entry to 'disable' a case instead of deleting the file; merging two fixtures and bringing stray cassettes along; typo in the manifest name changing the derived file name.

Related errors


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

Appendix: source

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

    if expected.is_empty() {
        if cases_directory.is_some() {
            bail!(
                "relationship verification failed: profile-only fixture has an undeclared cases directory"
            );
        }
        return Ok(Vec::new());
    }
    let directory = cases_directory.context(
        "relationship verification failed: manifest declares cases but the cases directory is missing",
    )?;

    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> {

View on GitHub (pinned to e846e6f4b4)