stamparm/maltrail · error

open pcap

Error message

open pcap

What it means

Panic from `.expect("open pcap")` when `capture::Handle::open_offline` fails to open the replay pcap file. The testkit replay path needs a real capture handle over the file, so an unreadable/invalid pcap aborts the test immediately.

Solutions

  1. Verify the pcap path exists and is readable before calling replay
  2. Use an absolute path or the repo_root()-based path helper instead of a relative path
  3. Regenerate the fixture pcap if it was truncated or corrupted
  4. Check open_offline's Err by matching instead of expect to surface the underlying cause

Example fix

// before
let mut handle = crate::capture::Handle::open_offline(pcap).expect("open pcap");
// after
let mut handle = match crate::capture::Handle::open_offline(pcap) {
    Ok(h) => h,
    Err(e) => panic!("open pcap {}: {e}", pcap.display()),
};
Defensive patterns

Strategy: validation

Validate before calling

let meta = std::fs::metadata(pcap).expect("pcap fixture missing");
assert!(meta.is_file() && meta.len() >= 24, "not a pcap file: {}", pcap.display());

Try / catch

let mut handle = crate::capture::Handle::open_offline(pcap)
    .unwrap_or_else(|e| panic!("open pcap {}: {e}", pcap.display()));

Prevention

When it happens

Trigger: `replay()` called with a path that does not exist, is not readable, or whose contents are not a valid pcap (bad magic, truncated header), causing `open_offline` to return Err.

Common situations: Wrong relative path for the fixture pcap; test fixture not committed or cleaned up by a previous run; a corrupted/hand-edited pcap file; running the test from a working directory other than the one the path assumes.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13). Data as JSON: /api/errors/3a26c1b804a25891. Report an issue: GitHub.

Appendix: source

Thrown at sensor/src/testkit.rs:194

    }

    /// Feed one raw packet (`ip_offset` is where the IP header starts).
    pub fn feed(&mut self, packet: &[u8], sec: u64, usec: u32, ip_offset: usize) {
        process::process_packet(&mut self.state, packet, sec, usec, ip_offset);
    }

    /// Feed a packet whose IP header starts at offset 0.
    pub fn feed_ip(&mut self, packet: &[u8], sec: u64) {
        self.feed(packet, sec, 0, 0);
    }

    /// Replay a pcap file through the real capture handle, DLT resolution and packet path
    /// (everything `worker::run` does, minus the thread and the housekeeping timers).
    ///
    /// Returns the number of packets read. `wallclock` selects the Python-3-compatible
    /// timestamp substitution.
    pub fn replay(&mut self, pcap: &std::path::Path, wallclock: bool) -> usize {
        let mut handle = crate::capture::Handle::open_offline(pcap).expect("open pcap");
        let datalink = handle.datalink();
        let snaplen = self.state.cfg.capture_snaplen;
        let mut count = 0usize;
        let mut scratch: Vec<u8> = Vec::new();
        loop {
            match handle.next_packet() {
                Ok(Some(captured)) => {
                    count += 1;
                    let data: &[u8] = if captured.data.len() > snaplen {
                        scratch.clear();
                        scratch.extend_from_slice(&captured.data[..snaplen]);
                        &scratch
                    } else {
                        captured.data
                    };
                    let (sec, usec) = if wallclock {
                        let now =
                            std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default();

View on GitHub (pinned to 77cfb06d76)