{"record":{"id":"e3f12a6150d37996","repo":"wasmerio/wasmer","slug":"journal-restore-error-failed-to-remove-file-fd","errorCode":null,"errorMessage":"journal restore error: failed to remove file (fd={fd}, path={path}) - {ret}","messagePattern":"journal restore error: failed to remove file \\(fd=(.+?), path=(.+?)\\) - (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/wasix/src/journal/effector/syscalls/path_unlink.rs","lineNumber":35,"sourceCode":"            JournalEntry::UnlinkFileV1 {\n                fd,\n                path: Cow::Owned(path),\n            },\n        )\n    }\n\n    pub fn apply_path_unlink(\n        ctx: &mut FunctionEnvMut<'_, WasiEnv>,\n        fd: Fd,\n        path: &str,\n    ) -> anyhow::Result<()> {\n        // see `VIRTUAL_ROOT_FD` for details as to why this exists\n        if fd == VIRTUAL_ROOT_FD {\n            ctx.data().state.fs.root_fs.remove_file(Path::new(path))?;\n        } else {\n            let ret = crate::syscalls::path_unlink_file_internal(ctx, fd, path)?;\n            if ret != Errno::Success {\n                bail!(\n                    \"journal restore error: failed to remove file (fd={fd}, path={path}) - {ret}\"\n                );\n            }\n        }\n        Ok(())\n    }\n}\n","sourceCodeStart":17,"sourceCodeEnd":43,"githubUrl":"https://github.com/wasmerio/wasmer/blob/8c4b9ee9d33fb2068863fbb3d328683e7e6ff7f5/lib/wasix/src/journal/effector/syscalls/path_unlink.rs#L17-L43","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: replaying a stale journal blindly\nlet mut journal = Journal::from_file(\"snapshot.journal\")?;\njournal.replay_all(ctx)?;\n// after: tolerate entries whose target no longer exists\nfor entry in journal.entries() {\n    match journal.replay_one(ctx, entry) {\n        Ok(()) => {}\n        Err(e) if e.to_string().contains(\"Errno::Noent\") => {\n            // file already absent; state already matches snapshot\n            continue;\n        }\n        Err(e) => return Err(e),\n    }\n}","handlingStrategy":"try-catch","validationCode":"// before replaying, check the target state\nfn unlink_target_present(fd_fdstat: &dyn Fn(u32) -> Result<(), Errno>, path: &str) -> bool {\n    // probe via fstatat-like check on the fs, or simply attempt a dry lookup;\n    // if the file is already absent, skip the journal entry\n    true // replace with real fs lookup\n}","typeGuard":null,"tryCatchPattern":"match journal.replay_one(ctx, entry) {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(\"failed to remove file\") => {\n        // inspect errno in message; ENOENT => treat as already-done and continue,\n        // anything else (EACCES, EISDIR, ENOTEMPTY) => abort restore\n        log::warn!(\"skipping stale unlink journal entry: {e}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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"],"tags":["wasix","journal","filesystem","snapshot-restore","errno"],"backgroundTag":"journal-restore-failed","analyzedSha":"8c4b9ee9d33fb2068863fbb3d328683e7e6ff7f5","analyzedAt":"2026-09-01T23:06:31.009Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}