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
- 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
- Verify the fd recorded in the journal actually refers to the directory containing the path at restore time; fd numbering can shift between runs
- Ensure the path's parent directory and the file itself are writable/removable by the process (not on a read-only mount)
- 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
- Snapshot the filesystem together with the journal so replay always starts from matching state
- Record and verify fd numbers in the journal against the restored fd table before applying entries
- Run replay on a writable filesystem — never restore journals onto read-only mounts
- Treat ENOENT during replay as idempotent success; treat other errnos as fatal divergence
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
- journal restore error: failed to open descriptor (fd={fd}, p
- journal restore error: failed to remove directory - {err}
- journal restore error: failed to rename path (old_fd={old_fd
- journal restore error: failed to set clock time (clock_id={c
- journal restore error: failed renumber file descriptor after
AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01).
Data as JSON: /api/errors/e3f12a6150d37996.
Report an issue: GitHub.