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
- Ensure the code path calling as_raw_fd uses a real timer: construct TimerFd from timerfd_create rather than the mock constructor.
- 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.
- Only enable the test-utils feature in dev-dependencies/test builds, not in release features, so Mock cannot be selected in production.
- 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
- Keep the test-utils feature out of release builds so Mock variants cannot be selected in production paths.
- Match on TimerFd before any fd-based API (epoll, poll) instead of blanket-calling as_raw_fd.
- Structure tests so polling paths use real timers and only time semantics are mocked.
- Add a debug_assert or typed accessor returning Option<RawFd> rather than panicking.
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
- Device is not initialized
- Error converting cpu id to Tid
- Error getting initial gdb event
- error unlocking vmm
- GDB requires KVM
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)