AprilNEA/OpenLogi · error

in-progress contribution contains unexpected entry

Error message

in-progress contribution contains unexpected entry {other:?}; refusing to publish over it

What it means

Before publishing the fixture manifest, `validate_resumable_layout` audits every entry of the contribution directory against a known allowlist (state file, profile file, manifest file, cases directory). This bail fires when the directory contains a named entry that is none of those — the command refuses to publish over a directory holding unexpected files, because the final publish removes the state file and writes manifests in place and could clobber or mix in unknown content.

Solutions

  1. Remove or move the unexpected entry named in the message out of the contribution directory, then rerun the command.
  2. Check for hidden files (`ls -la DIR`) — backup files and OS metadata like `.DS_Store` often trigger this.
  3. Keep the contribution directory used exclusively by `openlogi fixture contribute`; do all other work elsewhere.
  4. If the entry is expected content the tool should allow, verify whether it belongs under `cases/` instead of the top level.

Example fix

# before
ls fx/  ->  profile.json  state.json  notes.txt
openlogi fixture contribute --output fx ...
# error: unexpected entry "notes.txt"

# after
mv fx/notes.txt ~/notes.txt
openlogi fixture contribute --output fx ...   # publishes
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED: [&str; 4] = ["state.json", "profile.json", "manifest.json", "cases"];
fn has_foreign_entries(dir: &Path) -> anyhow::Result<bool> {
    Ok(fs::read_dir(dir)?
        .filter_map(Result::ok)
        .any(|e| !e.file_name().to_str().map_or(true, |n| ALLOWED.contains(&n))))
}
// clean up any foreign entry before invoking the finish/publish phase

Prevention

When it happens

Trigger: Running the publish/finish phase of `openlogi fixture contribute` (or the profile-only path) when DIR contains any extra file or subdirectory besides the expected state/profile/manifest/cases entries — e.g. editor backups, `.DS_Store`-style metadata, notes, or output from another tool written into the contribution directory.

Common situations: Editors or OS tools dropping backup/metadata files (`.DS_Store`, `profile.json~`, `Thumbs.db`) into DIR between step 1 and step 2; the user saving logs or notes inside the contribution folder; extracting an archive into DIR; a previous failed run leaving foreign files behind.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

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

    super::verify::run(&super::verify::VerifyArgs {
        directory: directory.to_path_buf(),
    })
}

fn validate_resumable_layout(directory: &Path, cassettes: &[HidCassette]) -> Result<()> {
    for entry in
        fs::read_dir(directory).context("could not inspect resumable contribution directory")?
    {
        let entry = entry.context("could not inspect resumable contribution entry")?;
        let name = entry.file_name();
        match name.to_str() {
            Some(STATE_FILE | PROFILE_FILE) => {}
            Some(MANIFEST_FILE) => require_regular_file(&entry.path(), "fixture manifest")?,
            Some(CASES_DIRECTORY) if !cassettes.is_empty() => {
                require_directory(&entry.path(), "fixture cases directory")?;
                validate_resumable_cases(&entry.path(), cassettes)?;
            }
            Some(other) => bail!(
                "in-progress contribution contains unexpected entry {other:?}; refusing to \
                 publish over it"
            ),
            None => bail!("in-progress contribution contains a non-UTF-8 entry"),
        }
    }
    Ok(())
}

fn validate_resumable_cases(directory: &Path, cassettes: &[HidCassette]) -> Result<()> {
    let expected = cassettes
        .iter()
        .map(|cassette| format!("{}.json", cassette.name))
        .collect::<Vec<_>>();
    for entry in fs::read_dir(directory).context("could not inspect resumable fixture cases")? {
        let entry = entry.context("could not inspect resumable fixture case")?;
        let name = entry.file_name();
        let Some(name) = name.to_str() else {

View on GitHub (pinned to e846e6f4b4)