wasmerio/wasmer · error

journal restore error: failed to remove file (fd={fd}, path=

Error message

journal restore error: failed to remove file (fd={fd}, path={path}) - {ret}

What it means

This error is thrown by apply_path_unlink when replaying a journal entry that unlinks (deletes) a file during WASIX snapshot restore. The journal effector calls path_unlink_file_internal on the given directory fd, and if that internal call returns any errno other than Success (e.g. ENOENT, EACCES, ENOTEMPTY, EISDIR), the restore aborts with this message including the failing fd, path, and errno.

Source

Thrown at lib/wasix/src/journal/effector/syscalls/path_unlink.rs:35

            JournalEntry::UnlinkFileV1 {
                fd,
                path: Cow::Owned(path),
            },
        )
    }

    pub fn apply_path_unlink(
        ctx: &mut FunctionEnvMut<'_, WasiEnv>,
        fd: Fd,
        path: &str,
    ) -> anyhow::Result<()> {
        // see `VIRTUAL_ROOT_FD` for details as to why this exists
        if fd == VIRTUAL_ROOT_FD {
            ctx.data().state.fs.root_fs.remove_file(Path::new(path))?;
        } else {
            let ret = crate::syscalls::path_unlink_file_internal(ctx, fd, path)?;
            if ret != Errno::Success {
                bail!(
                    "journal restore error: failed to remove file (fd={fd}, path={path}) - {ret}"
                );
            }
        }
        Ok(())
    }
}

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Check the errno in the message (e.g. Errno::Noent) — if ENOENT, the file is already gone and the restore target state diverged from the snapshot; restore against the original filesystem state or skip the stale journal entry
  2. Verify the fd recorded in the journal actually refers to the directory containing the path at restore time; fd numbering can shift between runs
  3. Ensure the path's parent directory and the file itself are writable/removable by the process (not on a read-only mount)
  4. If the target is actually a directory, use the directory-removal journal path instead of unlink

Example fix

// before: replaying a stale journal blindly
let mut journal = Journal::from_file("snapshot.journal")?;
journal.replay_all(ctx)?;
// after: tolerate entries whose target no longer exists
for entry in journal.entries() {
    match journal.replay_one(ctx, entry) {
        Ok(()) => {}
        Err(e) if e.to_string().contains("Errno::Noent") => {
            // file already absent; state already matches snapshot
            continue;
        }
        Err(e) => return Err(e),
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before replaying, check the target state
fn unlink_target_present(fd_fdstat: &dyn Fn(u32) -> Result<(), Errno>, path: &str) -> bool {
    // probe via fstatat-like check on the fs, or simply attempt a dry lookup;
    // if the file is already absent, skip the journal entry
    true // replace with real fs lookup
}

Try / catch

match journal.replay_one(ctx, entry) {
    Ok(()) => {}
    Err(e) if e.to_string().contains("failed to remove file") => {
        // inspect errno in message; ENOENT => treat as already-done and continue,
        // anything else (EACCES, EISDIR, ENOTEMPTY) => abort restore
        log::warn!("skipping stale unlink journal entry: {e}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Replaying a journal (resume-from-snapshot) whose recorded unlink targets a file that no longer exists, is a directory, is not writable/removable by the caller, or whose fd does not reference the containing directory. Note fd == VIRTUAL_ROOT_FD takes the root_fs path and never produces this error; only non-virtual-root fds hit the internal call.

Common situations: Restoring a snapshot against a different filesystem state than when it was captured (file already deleted, moved, or recreated as a directory); read-only or permission-restricted mounts; journal replay order issues with fat-fingered fd numbers stored in the journal.

Related errors


AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01). Data as JSON: /api/errors/e3f12a6150d37996. Report an issue: GitHub.