libnyanpasu/clash-nyanpasu · error

cannot complete materialization with a target hash mismatch

Error message

cannot complete materialization with a target hash mismatch

What it means

The complete() step detected that the hash of the file at the journal's managed_path no longer matches the hash recorded in the materialization journal. Complete is the final fencing check that the promoted content is exactly what was prepared; any divergence means something else modified the target after promote, and committing cleanup would silently accept corrupted or overwritten data.

Source

Thrown at backend/tauri/src/service/profile_file.rs:1667

                &root,
                operation_id,
                JournalLocation::FilePromoting,
                JournalLocation::FilePromoted,
            )?;
        }
        Ok(())
    }

    fn complete(&self, prepared: &PreparedMaterialization) -> anyhow::Result<()> {
        let root = self.ensure_materialization_layout()?;
        let operation_id = prepared.operation_id();
        let Some((mut location, journal)) = self.locate_materialization(&root, operation_id)?
        else {
            return Ok(());
        };
        let target = self.resolve(&journal.managed_path)?;
        if Self::path_hash(&target)? != journal.hash {
            bail!("cannot complete materialization with a target hash mismatch");
        }
        if location == JournalLocation::FilePromoting {
            Self::transition_journal(
                &root,
                operation_id,
                JournalLocation::FilePromoting,
                JournalLocation::FilePromoted,
            )?;
            location = JournalLocation::FilePromoted;
        }
        if !matches!(
            location,
            JournalLocation::StatePromoting | JournalLocation::FilePromoted
        ) {
            bail!("materialization is not in a completable phase");
        }
        Self::remove_operation_artifacts(
            &root,

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Re-run the whole sequence: compensate() (or remove the stale journal), then prepare -> promote -> complete with the new content, so the journal hash matches reality.
  2. Investigate what modified the managed path between promote and complete and lock/serialize writes to that path.
  3. Do NOT call complete() after mutating the target yourself; the fence is intentional — restart the materialization instead.
  4. Ensure only one pipeline writes a given managed profile path at a time (actor/queue per path).

Example fix

// before
service.write_external_edit(&path, edited)?; // target diverged
service.complete(&prepared)?; // hash mismatch -> bail

// after
service.compensate(&prepared)?; // or remove stale journal
let prepared = service.prepare_state_first(&path, resource, new_revision)?;
service.promote(&prepared)?;
service.complete(&prepared)?;
Defensive patterns

Strategy: try-catch

Validate before calling

let target = service.resolve(&journal_managed_path)?;
let current = hash_file(&target)?;
let expected = /* hash recorded in the journal */;
anyhow::ensure!(current == expected, "target {} diverged before complete; restart materialization", target.display());

Try / catch

match service.complete(&prepared) {
    Err(e) if e.to_string().contains("hash mismatch") => {
        // target was modified externally; restart the operation with fresh content
        let prepared = service.prepare_state_first(&path, resource, new_revision)?;
        service.promote(&prepared)?;
        service.complete(&prepared)?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling complete(prepared) when Self::path_hash(target) != journal.hash: the managed file was edited (by the user, an editor, or another service) between promote and complete; promote's promote_resource silently failed to write the expected bytes; a different profile write raced and replaced the file; the resolve() target differs from what prepare hashed.

Common situations: User saves the profile file in an external editor while an update/apply pipeline is mid-flight; two concurrent profile operations targeting the same path; antivirus or sync tools rewriting the file; a symlink target changed so resolve() now reads different content.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/9ebf79447c1aacad. Report an issue: GitHub.