firecracker-microvm/firecracker · error

Mocked timer doesn't support fd

Error message

Mocked timer doesn't support fd

What it means

TimerFd is an enum with a Real(file) variant (backed by an actual timerfd) and a Mock(_) variant used only in tests to make time-based tests deterministic. A mocked timer has no kernel file descriptor behind it, so AsRawFd::as_raw_fd cannot return a RawFd and panics with "Mocked timer doesn't support fd" instead of returning a Result. This is an intentional, fail-fast misuse guard: any code path that needs a real fd must not run against a mocked clock.

Solutions

  1. Ensure the code path calling as_raw_fd uses a real timer: construct TimerFd from timerfd_create rather than the mock constructor.
  2. Match on the TimerFd enum before extracting the fd and handle the Mock case explicitly (skip epoll registration or return an error) instead of relying on AsRawFd.
  3. Only enable the test-utils feature in dev-dependencies/test builds, not in release features, so Mock cannot be selected in production.
  4. Restructure tests so time is mocked at a higher level (clock abstraction) while fd-based polling paths use real timers.

Example fix

// before
let fd = timer.as_raw_fd(); // panics if TimerFd::Mock
// after
let fd = match timer {
    TimerFd::Real(f) => f.as_raw_fd(),
    TimerFd::Mock(_) => return Err(MyError::MockTimerHasNoFd),
};
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_real_timer(t: &TimerFd) -> bool { matches!(t, TimerFd::Real(_)) }

Type guard

fn as_real_fd(timer: &TimerFd) -> Option<RawFd> {
    match timer {
        TimerFd::Real(f) => Some(f.as_raw_fd()),
        TimerFd::Mock(_) => None,
    }
}

Try / catch

// Rust panics are not catchable via Result; use std::panic::catch_unwind only at boundaries.
let fd = match timer { TimerFd::Real(f) => f.as_raw_fd(), TimerFd::Mock(_) => return Err(EpollError::MockTimer) };

Prevention

When it happens

Trigger: Calling .as_raw_fd() (e.g. to register the timer with epoll/poll) on a TimerFd constructed as TimerFd::Mock, i.e. when the crate is built with the test or test-utils feature (or under cfg(test)) and a mocked clock/timer was injected.

Common situations: Running production-style event-loop or polling code in tests with a mocked timer; wiring a TimerFd obtained from a test fixture into an epoll registration helper; accidentally enabling the test-utils feature in a release build so Mock variants exist and get selected.

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 firecracker-microvm/firecracker@23b09b943f (2026-09-19). Data as JSON: /api/errors/41b275fa656b7a6e. Report an issue: GitHub.

Appendix: source

Thrown at src/utils/src/time.rs:328

        // SAFETY: Zero init of a PDO type.
        let mut spec: libc::itimerspec = unsafe { std::mem::zeroed() };
        // SAFETY: Safe because timerfd_gettime is trusted to only modify `spec`.
        let ret = unsafe { libc::timerfd_gettime(file.as_raw_fd(), &mut spec) };
        assert!(
            0 <= ret,
            "TimerFd arm failed: {:#}",
            std::io::Error::last_os_error()
        );
        spec.it_value.tv_sec != 0 || spec.it_value.tv_nsec != 0
    }
}

impl AsRawFd for TimerFd {
    fn as_raw_fd(&self) -> RawFd {
        match self {
            TimerFd::Real(file) => file.as_raw_fd(),
            #[cfg(any(test, feature = "test-utils"))]
            TimerFd::Mock(_) => panic!("Mocked timer doesn't support fd"),
        }
    }
}

/// Test-only mock clock and timer, used to make time-based unit tests deterministic.
///
/// Gated behind `#[cfg(any(test, feature = "test-utils"))]` at the module level so the individual
/// items don't each need the attribute. Re-exported from the parent so callers keep using
/// [`MockClock`]/[`MockTimer`] directly.
#[cfg(any(test, feature = "test-utils"))]
mod mock {
    use std::sync::{Arc, Mutex};
    use std::time::{Duration, Instant};

    /// A controllable virtual clock for tests. Starts at the current real time, but only moves
    /// forward via [`MockClock::advance`]. Cheap to [`Clone`]; all clones share the same
    /// underlying time, so multiple consumers advance together.
    #[derive(Debug, Clone)]

View on GitHub (pinned to 23b09b943f)