{"record":{"id":"bc5bcb311d24c935","repo":"FuelLabs/fuel-core","slug":"failed-to-create-the-local-runner","errorCode":null,"errorMessage":"Failed to create the local runner","messagePattern":"Failed to create the local runner","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/services/importer/src/importer.rs","lineNumber":494,"sourceCode":"struct VerifyAndExecutionResult {\n    tx_status: Vec<TransactionExecutionStatus>,\n    events: Vec<Event>,\n    changes: Changes,\n}\n\nstruct PrepareImportResult {\n    result: UncommittedResult<Changes>,\n    block_changes: Changes,\n}\n\nimpl<IDatabase, E, V> ImporterInner<IDatabase, E, V>\nwhere\n    IDatabase: ImporterDatabase + Transactional,\n    E: Validator,\n    V: BlockVerifier,\n{\n    async fn run(&mut self) {\n        let local_runner = LocalRunner::new().expect(\"Failed to create the local runner\");\n        while let Some(command) = self.commands.recv().await {\n            match command {\n                Commands::Stop => break,\n                Commands::CommitResult {\n                    result,\n                    permit,\n                    callback,\n                } => {\n                    let result = self.commit_result(&local_runner, permit, result);\n                    let _ = callback.send(result);\n                }\n                #[cfg(test)]\n                Commands::VerifyAndExecuteBlock {\n                    sealed_block,\n                    callback,\n                } => {\n                    let result =\n                        self.verify_and_execute_block(&local_runner, sealed_block);","sourceCodeStart":476,"sourceCodeEnd":512,"githubUrl":"https://github.com/FuelLabs/fuel-core/blob/add100d30d21498e8528c46be8567fbd2ea019af/crates/services/importer/src/importer.rs#L476-L512","documentation":"In `ImporterInner::run`, `LocalRunner::new()` builds a dedicated 2-thread rayon thread pool used for block execution and serialization. The result is unwrapped with `.expect(\"Failed to create the local runner\")`, so this message is a panic, not a returned error. It fires only when rayon's `ThreadPoolBuilder::build()` fails, which is practically limited to thread-creation failures (e.g. resource limits) — rayon only errors if its internal spawn fails.","triggerScenarios":"`Importer::run` starts (importer.rs:494) and `rayon::ThreadPoolBuilder::new().num_threads(2).build()` fails, typically because the OS refused to spawn threads (hit `RLIMIT_NPROC`/`ulimit -u`, cgroup pids limit, or memory exhaustion for thread stacks).","commonSituations":"Running fuel-core in restricted containers/Kubernetes with low `pids.max` or `ulimit -u`; deeply nested rayon pools in environments that disallow thread creation; heavily loaded hosts at the process/thread limit.","solutions":["Raise the process/thread limit: `ulimit -u` on Linux or the container's pids cgroup limit (`--pids-limit` in Docker, `pids.max` in Kubernetes).","Reduce overall thread pressure: lower rayon global thread counts (`RAYON_NUM_THREADS`) and other concurrency settings so thread creation succeeds.","Check host memory — each thread needs stack space; free memory or lower stack sizes if the failure is OOM at spawn.","If failures persist, replace the `.expect` with propagated error handling so the importer reports the root `rayon` error instead of panicking."],"exampleFix":"// before\nlet local_runner = LocalRunner::new().expect(\"Failed to create the local runner\");\n// after\nlet local_runner = LocalRunner::new().map_err(|e| {\n    anyhow::anyhow!(\"Failed to create the local runner (check thread/memory limits): {e:#}\")\n})?;","handlingStrategy":"validation","validationCode":"// verify the process can spawn threads before starting the importer\nlet probe = std::thread::Builder::new().stack_size(64 * 1024).spawn(|| {});\nif probe.is_err() {\n    return Err(anyhow!(\"Cannot create threads (check ulimit -u / pids cgroup limit / memory)\"));\n}","typeGuard":"fn can_create_rayon_pool(threads: usize) -> bool {\n    rayon::ThreadPoolBuilder::new().num_threads(threads).build().is_ok()\n}","tryCatchPattern":"// the source panics via expect, so defend before calling run()\nif !can_create_rayon_pool(2) {\n    return Err(anyhow!(\"Failed to create the local runner: thread pool unavailable\"));\n}\nimporter.run().await;","preventionTips":["Raise `ulimit -u` and container pids limits for fuel-core deployments.","Set RAYON_NUM_THREADS and pool sizes within the environment's thread budget.","Ensure sufficient memory for thread stacks in constrained containers.","Replace `.expect` with propagated errors in importer startup to fail gracefully instead of panicking."],"tags":["panic","rayon","thread-pool","resource-limits"],"backgroundTag":"thread-creation-failed","analyzedSha":"add100d30d21498e8528c46be8567fbd2ea019af","analyzedAt":"2026-09-05T18:46:12.018Z","contentChangedAt":"2026-09-05T18:46:12.018Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}