libnyanpasu/clash-nyanpasu · error
materialization is not in a completable phase
Error message
materialization is not in a completable phase
What it means
complete() was invoked while the journal is in a phase that cannot be finalized. Only StatePromoting and FilePromoted journals are completable; anything else (e.g. still prepared, or a phase that was never transitioned) is rejected to preserve the two-phase commit state machine. This indicates promote/transition steps were skipped or the journal is stale.
Source
Thrown at backend/tauri/src/service/profile_file.rs:1682
};
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,
operation_id,
&Self::journal_path(&root, location, operation_id),
)
}
fn compensate(&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 compensating = location.compensating();
if location != compensating {
Self::transition_journal(&root, operation_id, location, compensating)?;View on GitHub (pinned to f7dbce2997)
Solutions
- Always call promote(prepared) before complete(prepared) so the journal reaches StatePromoting/FilePromoted.
- For a stale journal in a non-completable phase, run compensate() to roll it back, then re-prepare the operation.
- Verify the recovery/resume logic only drives journals from completable phases, routing others through the appropriate transition first.
- Inspect the journal files under the materialization root to confirm the actual phase before resuming an interrupted operation.
Example fix
// before let prepared = service.prepare_state_first(&path, resource, rev)?; service.complete(&prepared)?; // skipped promote -> not completable // after let prepared = service.prepare_state_first(&path, resource, rev)?; service.promote(&prepared)?; service.complete(&prepared)?;
Defensive patterns
Strategy: validation
Validate before calling
if let Ok(Some((location, _journal))) = service.locate_materialization(&root, prepared.operation_id()) {
anyhow::ensure!(
matches!(location, JournalLocation::StatePromoting | JournalLocation::FilePromoted),
"operation {} in phase {:?} is not completable; promote first or compensate",
prepared.operation_id(), location
);
} Type guard
fn is_completable(loc: &JournalLocation) -> bool {
matches!(loc, JournalLocation::StatePromoting | JournalLocation::FilePromoted)
} Try / catch
if let Err(e) = service.complete(&prepared) {
if e.to_string().contains("not in a completable phase") {
service.promote(&prepared)?; // advance the journal first
service.complete(&prepared)?;
} else { return Err(e); }
} Prevention
- Enforce the full sequence prepare -> promote -> complete; never skip promote.
- In recovery code, drive each journal phase through its designated transition before completing.
- Check the journal phase before resuming an interrupted operation.
- Call complete() only once per operation; rely on its idempotent no-op for missing journals.
When it happens
Trigger: Calling complete(prepared) without a prior promote() for a journal left in a prepared/pre-promoting phase that did not auto-transition (e.g. a FilePrepared/StatePrepared journal whose promoting() transition path was not exercised), or completing a journal whose phase enum falls outside {StatePromoting, FilePromoted} due to recovery from a partially written journal.
Common situations: Calling complete() directly after prepare() skipping promote(); crash-recovery resuming with a journal in an unexpected phase; calling complete() twice (second call: artifacts removed so locate returns None and it no-ops — but a leftover journal in another phase hits this bail); mixing file-first and state-first flows and completing at the wrong step.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- cannot promote a compensating materialization
- materialization journal not found for operation {operation_i
- cannot complete materialization with a target hash mismatch
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/7d38ad31eea10815.
Report an issue: GitHub.