{"record":{"id":"35f45321ea3289cd","repo":"stamparm/maltrail","slug":"spawn-capture-worker","errorCode":null,"errorMessage":"spawn capture worker","messagePattern":"spawn capture worker","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"sensor/src/main.rs","lineNumber":660,"sourceCode":"        handles.into_iter().map(|(h, _, _)| vec![(h, String::new())]).collect()\n    };\n\n    let mut threads = Vec::with_capacity(worker_handles.len());\n    for (id, group) in worker_handles.drain(..).enumerate() {\n        let ctx = WorkerContext {\n            id,\n            cfg: cfg.clone(),\n            whitelist: whitelist.clone(),\n            store: store.clone(),\n            output: output_cfg.clone(),\n            slot: registry.slots[id].clone(),\n            shutdown: shutdown.clone(),\n        };\n        threads.push(\n            std::thread::Builder::new()\n                .name(format!(\"capture-{id}\"))\n                .spawn(move || worker::run_all(group, ctx))\n                .expect(\"spawn capture worker\"),\n        );\n    }\n\n    // Watch for a signal while the workers run.\n    let watcher_shutdown = shutdown.clone();\n    std::thread::Builder::new()\n        .name(\"signals\".into())\n        .spawn(move || loop {\n            if SHUTDOWN.load(Ordering::Relaxed) {\n                watcher_shutdown.store(true, Ordering::Relaxed);\n                break;\n            }\n            if watcher_shutdown.load(Ordering::Relaxed) {\n                break;\n            }\n            std::thread::sleep(Duration::from_millis(100));\n        })\n        .ok();","sourceCodeStart":642,"sourceCodeEnd":678,"githubUrl":"https://github.com/stamparm/maltrail/blob/77cfb06d7606506d101bbcec0786c77166c4255e/sensor/src/main.rs#L642-L678","documentation":"In run(), each capture worker thread is spawned with std::thread::Builder::spawn(...).expect(\"spawn capture worker\"). spawn only returns Err when the OS refuses to create the thread (out of resources: thread count, memory for the stack, or RLIMIT_NPROC/permissions), so this panic means the sensor cannot start its capture pipeline and it aborts instead of running degraded.","triggerScenarios":"spawn returns Err on systems with exhausted thread/PID limits, insufficient memory for per-thread stacks, container cgroup/pids limits, or seccomp policies blocking clone().","commonSituations":"Running many sensors per host so the process/thread limit is hit; hardened containers with low pids.max; low-memory VMS.","solutions":["Raise the process/thread limit (ulimit -u, container pids.max) or reduce CAPTURE_WORKERS to spawn fewer threads","Free memory or lower per-thread stack size via thread::Builder::stack_size","Inspect the wrapped io::Error from the panic message to identify the exact OS resource that failed"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"let n_workers = cfg.capture_workers;\nlet limits = check_thread_headroom(n_workers); // compare against ulimit -u / cgroup pids.max before spawning","typeGuard":null,"tryCatchPattern":"match builder.spawn(move || worker::run_all(group, ctx)) {\n    Ok(handle) => threads.push(handle),\n    Err(e) => eprintln!(\"cannot spawn capture worker: {e}; reducing workers or aborting\"),\n}","preventionTips":["Size CAPTURE_WORKERS within host thread/PID limits","Check container pids.max and ulimit -u before deployment","Handle spawn errors gracefully instead of expect in production paths"],"tags":["rust","threads","resources"],"backgroundTag":"thread-spawn-failed","analyzedSha":"77cfb06d7606506d101bbcec0786c77166c4255e","analyzedAt":"2026-09-13T03:50:16.010Z","contentChangedAt":"2026-09-13T03:50:16.010Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}