rustfs/rustfs · info · std::io::Error
Interrupted
Interrupted
Error message
tier free-version recovery cancelled
What it means
Free-version recovery walks tier free-version records with a cancellation token; when the token fires (node shutdown, task cancellation, operator abort) mid-walk, the scan stops and returns this Interrupted error instead of partial stats. Stats accumulated so far are discarded in favor of an explicit signal, and the retry markers (retry_cursor / next_bucket_marker / next_object_marker) exist so the walk can resume where it stopped. This is an expected, benign shutdown signal — not a fault.
Source
Thrown at crates/ecstore/src/bucket/lifecycle/tier_free_version_recovery.rs:216
let mut retry_cursor = RetryCursor::new(bucket_marker, object_marker);
for oi in page.items {
if cancel_token.is_cancelled() {
return Err(tier_free_version_recovery_cancelled());
}
retry_cursor.visit(&oi);
if !record_recovered_free_version_enqueue(&mut stats, enqueue_recovered_free_version(&api, oi).await) {
let (bucket_marker, object_marker) = retry_cursor.retry_markers();
stats.truncated = true;
stats.next_bucket_marker = bucket_marker;
stats.next_object_marker = object_marker;
break;
}
}
Ok(stats)
}
fn tier_free_version_recovery_cancelled() -> crate::error::Error {
std::io::Error::new(std::io::ErrorKind::Interrupted, "tier free-version recovery cancelled").into()
}
fn tier_free_version_recovery_walk_shutdown_timed_out() -> crate::error::Error {
std::io::Error::new(
std::io::ErrorKind::TimedOut,
"tier free-version recovery walk did not stop after cancellation",
)
.into()
}
fn record_recovered_free_version_enqueue(stats: &mut FreeVersionRecoveryStats, queued: bool) -> bool {
stats.scanned += 1;
if queued {
stats.enqueued += 1;
true
} else {
stats.failed += 1;View on GitHub (pinned to 5dca076efe)
Solutions
- Nothing to repair — restart or re-invoke recovery and pass the previous next_bucket_marker/next_object_marker to resume
- If shutdowns repeatedly interrupt long walks, raise the per-run limit or schedule recovery during quiet windows
- Distinguish from TimedOut sibling error: this one means cancellation worked correctly
Defensive patterns
Strategy: retry
Validate before calling
// Pass through the cancellation token and check it before starting
if cancel_token.is_cancelled() {
return Ok(default_stats()); // skip the run instead of eating an Interrupted
}
let stats = recover_tier_free_versions(api, limit, marker, &cancel_token).await?; Type guard
fn is_cancellation(e: &crate::error::Error) -> bool {
matches!(e.io_error_kind(), Some(std::io::ErrorKind::Interrupted))
&& e.to_string().contains("recovery cancelled")
} Try / catch
match recover_tier_free_versions(api, limit, marker, &cancel).await {
Err(e) if is_cancellation(&e) => {
// benign shutdown: persist next_bucket_marker/next_object_marker and resume later
Ok(partial)
}
other => other,
} Prevention
- Persist the retry markers so interrupted walks resume instead of restarting
- Do not alert on Interrupted during planned restarts; alert on the TimedOut sibling instead
- Size the per-run limit so walks finish within maintenance windows
When it happens
Trigger: recover/free-version scan is running when cancel_token.cancelled() resolves (select! at line 310), the loop checks is_cancelled() between items (line 189/319), or the walker channel closes due to cancellation (line 422/438). Any graceful shutdown or cancel during an active recovery produces it.
Common situations: Rolling restarts while the lifecycle free-version scanner is mid-bucket; cancellation triggered by reconfiguration of lifecycle/tier settings; CI timeouts cancelling the recovery task.
Related errors
AI-assisted analysis of rustfs/rustfs@5dca076efe (2026-08-20).
Data as JSON: /api/errors/d171f66d2a3028e2.
Report an issue: GitHub.