astrid-runtime/astrid · warning

No-CoW workspace does not support rollback

Error message

No-CoW workspace does not support rollback

What it means

The No-CoW workspace strategy writes directly to the workspace, so no staged changes exist to discard. rollback on this backend deliberately returns an Unsupported error instead of pretending to undo writes that already landed in the workspace.

Solutions

  1. Only call rollback when the workspace is in CoW mode; check the strategy first
  2. Enable CoW (APFS clonefile-capable volume, feature enabled) if rollback semantics are required
  3. Accept that with No-CoW, writes are already durable and cannot be undone — compensate at a higher level
  4. Treat io::ErrorKind::Unsupported from rollback as 'nothing to roll back' in generic recovery code

Example fix

// before
if let Err(e) = task { workspace.rollback()?; }
// after
if let Err(e) = task {
    if workspace.supports_cow() {
        workspace.rollback()?;
    } else {
        tracing::warn!("No-CoW workspace: cannot roll back, writes were direct");
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

fn rollback_if_cow(ws: &dyn WorkspaceBackend) -> io::Result<()> {
    if ws.strategy() == Strategy::NoCow {
        tracing::warn!("No-CoW: nothing to roll back");
        return Ok(());
    }
    ws.rollback()
}

Type guard

fn supports_rollback(ws: &dyn WorkspaceBackend) -> bool {
    ws.strategy() != Strategy::NoCow
}

Try / catch

match ws.rollback() {
    Err(e) if e.kind() == io::ErrorKind::Unsupported => {
        tracing::warn!("No-CoW workspace: writes cannot be rolled back");
        // escalate: manual cleanup or compensation at a higher level
        Ok(())
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling rollback() on a workspace running the No-CoW strategy (CoW unavailable or disabled), typically from an error-handling path that assumes staged writes.

Common situations: Generic error-recovery code that unconditionally calls rollback; environments where clonefile is unavailable so the backend silently fell back to No-CoW; CoW disabled in deployment config.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/6a53c115b7d47a49. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-vfs/src/workspace_cow/mod.rs:163

    }

    fn promote(&self) -> io::Result<()> {
        tracing::warn!(
            "workspace CoW: promote requested on a No-CoW workspace — writes already \
             went direct to the workspace, there is nothing to commit"
        );
        Err(io::Error::new(
            io::ErrorKind::Unsupported,
            "No-CoW workspace does not support promote",
        ))
    }

    fn rollback(&self) -> io::Result<()> {
        tracing::warn!(
            "workspace CoW: rollback requested on a No-CoW workspace — writes already \
             went direct to the workspace and cannot be rolled back"
        );
        Err(io::Error::new(
            io::ErrorKind::Unsupported,
            "No-CoW workspace does not support rollback",
        ))
    }

    fn teardown(&self) {}
}

/// Construct the preferred copy-on-write backend for this host, storing its
/// working trees under caller-selected disposable scratch (the capsule runtime
/// uses the boot-cleaned `run/hosted-workspace-cow` tree).
///
/// The factory only *selects* a backend; the mount/clone happens in
/// [`prepare`](WorkspaceCow::prepare). Selection order:
/// * macOS → [`ApfsCow`](apfs::ApfsCow).
/// * Linux → `OverlayfsCow` (which tries a native `overlayfs` mount, then
///   `fuse-overlayfs`, at prepare time).
/// * any other platform → [`NoCow`], with a `warn` naming the reason.

View on GitHub (pinned to affd8760f4)