jdx/mise · error
incoming application requires an active recovery operation
Error message
incoming application requires an active recovery operation
What it means
validate_starting_head confirms an incoming (pulled) history application is tied to an active recovery operation holding a shared writer. If no recovery operation is active (self.0 is None), applying incoming data is an invalid state, so it bails.
Solutions
- Start the recovery/apply flow through the public API that takes the recovery lock first, then re-run pull/apply
- Re-run pull to plan and apply in one properly locked session
- Check that no earlier step aborted and cleared the recovery operation before apply
Example fix
// before: apply without active recovery scope.validate_starting_head(Some(head))?; // after: enter recovery first let scope = HistoryScope::begin_recovery(&state_dir)?; // acquires lock scope.validate_starting_head(Some(head))?;
Defensive patterns
Strategy: validation
Validate before calling
if !history.recovery_active() {
history.begin_recovery()?; // acquire recovery lock before validating
} Try / catch
match scope.validate_starting_head(head) {
Err(e) if e.to_string().contains("active recovery operation") => {
scope.begin_recovery()?;
scope.validate_starting_head(head)?;
}
other => other?,
} Prevention
- Always run plan/apply within one locked recovery session
- Don't drop or finish the recovery state between steps
- Use the public pull/apply API instead of calling internals ad hoc
When it happens
Trigger: Calling validate_starting_head on a scope whose shared recovery state was never initialized (no active recovery operation) — e.g. applying a pull's incoming changes without first acquiring/taking the recovery lock, or after the recovery state was dropped/finished.
Common situations: Scripting history pull/apply calls out of order; a crash or earlier error cleared the recovery state between plan and apply; running the apply step in a fresh process that never opened the recovery operation.
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
- an operation record always has an operation
- another history operation is running
- another history operation is running
- a process it started kept its output open
- another daemon profile is active for
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/dab839af21281e5f.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/scope.rs:250
.collect();
writer.pending.checkpoint.labels = label.into_iter().map(str::to_owned).collect();
writer.operation_mut().message = label.map(str::to_owned);
if let Err(err) = writer.write_pending() {
warn!("history: could not persist the capture label: {err:#}");
}
}
}
/// The protective checkpoint this operation took, if any.
pub(crate) fn before(&self) -> Option<(u64, String)> {
self.0
.as_ref()
.and_then(|shared| lock_unpoisoned(shared).before.clone())
}
pub(crate) fn validate_starting_head(&self, expected: Option<&str>) -> Result<()> {
let Some(shared) = &self.0 else {
eyre::bail!("incoming application requires an active recovery operation");
};
let writer = lock_unpoisoned(shared);
if writer.starting_head.as_deref() != expected {
eyre::bail!(
"local saved history changed since planning; nothing was applied; run pull again"
);
}
Ok(())
}
/// Retakes the protective checkpoint, replacing the earlier one, when
/// files changed between it and the verified plan.
pub(crate) fn recapture_before(&self, paths: &[PathBuf]) -> Result<()> {
let Some(shared) = &self.0 else {
return Ok(());
};
let mut writer = lock_unpoisoned(shared);
// held across the reservation, the capture, and the removal: theView on GitHub (pinned to 533346cc37)