wasmerio/wasmer · error

journal restore error: failed renumber file descriptor after

Error message

journal restore error: failed renumber file descriptor after epoll create (from={}, to={}) - {}

What it means

apply_epoll_create creates an epoll instance during journal replay, then calls fd_renumber_internal to move the newly allocated fd to the fd recorded in the journal. If the renumber fails, this error fires, meaning the restored process's fd table could not match the snapshotted layout.

Source

Thrown at lib/wasix/src/journal/effector/syscalls/epoll_create.rs:19

use super::*;

impl JournalEffector {
    pub fn save_epoll_create(ctx: &mut FunctionEnvMut<'_, WasiEnv>, fd: Fd) -> anyhow::Result<()> {
        Self::save_event(ctx, JournalEntry::EpollCreateV1 { fd })
    }

    pub fn apply_epoll_create(ctx: &mut FunctionEnvMut<'_, WasiEnv>, fd: Fd) -> anyhow::Result<()> {
        let ret_fd = crate::syscalls::epoll_create_internal(ctx, Some(fd))
            .map_err(|err| {
                anyhow::format_err!("journal restore error: failed to create epoll - {err}")
            })?
            .map_err(|err| {
                anyhow::format_err!("journal restore error: failed to create epoll - {err}")
            })?;

        let ret = crate::syscalls::fd_renumber_internal(ctx, ret_fd, fd);
        if !matches!(ret, Ok(Errno::Success)) {
            bail!(
                "journal restore error: failed renumber file descriptor after epoll create (from={}, to={}) - {}",
                ret_fd,
                fd,
                ret.unwrap_or(Errno::Unknown)
            );
        }

        Ok(())
    }
}

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Take a fresh snapshot and retry the restore.
  2. Inspect the fd table of the restoring environment for conflicts at the target fd number.
  3. Use the same wasix runtime version for snapshot and restore.
  4. Verify the journal is complete and not truncated/corrupted.
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the target fd slot is free before restore replay
// (conceptual check: rebuild fd table expectations from the journal header)
let expected_fds: Vec<u32> = journal.fd_allocations();
if expected_fds.iter().any(|fd| env.fd_in_use(*fd)) {
    return Err(anyhow::anyhow!("fd conflict during restore; reset fd table first"));
}

Try / catch

match restore_from_journal(&env, &journal) {
    Err(e) if e.to_string().contains("epoll create") => {
        eprintln!("journal restore fd conflict: {e}; retaking snapshot recommended");
        retake_snapshot_and_resume()?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Journal restore replaying an EpollCreate entry when fd_renumber_internal fails (e.g. target fd already occupied/invalid, or internal fd allocation returned an unexpected fd).

Common situations: Restoring a snapshot whose fd table conflicts with fds opened during restore setup; corrupted or partially-applied journals; runtime version mismatch between snapshot creation and restore.

Related errors


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