firecracker-microvm/firecracker · error

Device is not initialized

Error message

Device is not initialized

What it means

Panic from `expect("Device is not initialized")` in `Vsock::signal_used_queue` (src/vmm/src/devices/virtio/vsock/device.rs:141). `self.device_state.active_state()` returns `Some` only after the guest virtio driver has activated the device ( FEATURES_OK/DRIVER_OK handshake completed and the device moved to Activated). If any code path signals the used queue before activation, `active_state()` is None and the process aborts.

Solutions

  1. Make sure the vsock backend event handler is only registered/started after the device receives DRIVER_OK (check `device_state.active_state().is_some()` before signaling)
  2. If restoring from a snapshot, verify the restore path calls `activate()` on the vsock device before resuming queue/event processing
  3. In library code, replace the `expect` with a graceful branch: `match self.device_state.active_state() { Some(s) => s.interrupt.trigger(..), None => { warn!(...); Ok(()) } }`
  4. If you drive the device from tests, activate it with a mock interrupt/queue pair first

Example fix

// before
self.device_state
    .active_state()
    .expect("Device is not initialized")
    .interrupt
    .trigger(VirtioInterruptType::Queue(qidx.try_into().unwrap_or_else(|_| panic!("vsock: invalid queue index: {qidx}"))))
    .map_err(DeviceError::FailedSignalingIrq)

// after
if let Some(state) = self.device_state.active_state() {
    state.interrupt
        .trigger(VirtioInterruptType::Queue(qidx as u16))
        .map_err(DeviceError::FailedSignalingIrq)
} else {
    warn!("vsock: signal_used_queue before activation (qidx {qidx})");
    Ok(())
}
Defensive patterns

Strategy: validation

Validate before calling

// Only let the backend notify queues once the guest driver activated the device
if vsock.device_state().active_state().is_some() {
    vsock.signal_used_queue(qidx)?;
} else {
    warn!("vsock not activated; deferring queue signal");
}

Type guard

fn is_vsock_activated(dev: &Vsock<impl VsockBackend>) -> bool {
    dev.device_state().active_state().is_some()
}

Try / catch

// Panics cannot be caught with Result; as a last resort isolate third-party threads:
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| vsock.signal_used_queue(0)));
if r.is_err() { /* device not initialized or irq failure: restart device */ }

Prevention

When it happens

Trigger: Calling `signal_used_queue(qidx)` (or the backend doing so indirectly) before the guest driver finished the virtio activation handshake: e.g. a host-side vsock peer connects and pushes data while the device is still in `Inactive`/`Created` state, or during snapshot-restore before `activate()` has run.

Common situations: Guest boots a kernel without a virtio-vsock driver (device never activates) while the host backend already delivers RX data; restoring from a snapshot where the backend/event handler threads start before the device is re-activated; tests that instantiate the device and drive queues directly without an activate step.

Related errors


AI-assisted analysis of firecracker-microvm/firecracker@81b38b9dad (2026-08-19). Data as JSON: /api/errors/e7fe63c5b518b10a. Report an issue: GitHub.

Appendix: source

Thrown at src/vmm/src/devices/virtio/vsock/device.rs:139

        Self::with_queues(cid, backend, queues)
    }

    /// Retrieve the cid associated with this vsock device.
    pub fn cid(&self) -> u64 {
        self.cid
    }

    /// Access the backend behind the device.
    pub fn backend(&self) -> &B {
        &self.backend
    }

    /// Signal the guest driver that we've used some virtio buffers that it had previously made
    /// available.
    pub fn signal_used_queue(&self, qidx: usize) -> Result<(), DeviceError> {
        self.device_state
            .active_state()
            .expect("Device is not initialized")
            .interrupt
            .trigger(VirtioInterruptType::Queue(qidx.try_into().unwrap_or_else(
                |_| panic!("vsock: invalid queue index: {qidx}"),
            )))
            .map_err(DeviceError::FailedSignalingIrq)
    }

    /// Signal the guest which queues are ready to be consumed
    pub fn signal_used_queues(&self, used_queues: &[u16]) -> Result<(), DeviceError> {
        self.device_state
            .active_state()
            .expect("Device is not initialized")
            .interrupt
            .trigger_queues(used_queues)
            .map_err(DeviceError::FailedSignalingIrq)
    }

    /// Walk the driver-provided RX queue buffers and attempt to fill them up with any data that we

View on GitHub (pinned to 81b38b9dad)