{"record":{"id":"fac34b0b355f5484","repo":"astrid-runtime/astrid","slug":"failed-to-spawn-hook-epoch-ticker","errorCode":null,"errorMessage":"failed to spawn hook epoch ticker","messagePattern":"failed to spawn hook epoch ticker","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-hooks/src/handler/wasm.rs","lineNumber":126,"sourceCode":"impl WasmHandler {\n    /// Create a new WASM handler.\n    #[must_use]\n    pub(crate) fn new(workspace_root: PathBuf) -> Self {\n        let engine = build_hook_engine();\n\n        // Spawn epoch ticker so that epoch deadlines on Store actually fire.\n        let epoch_stop = Arc::new(std::sync::atomic::AtomicBool::new(false));\n        let stop_clone = epoch_stop.clone();\n        let ticker_engine = engine.clone();\n        let epoch_handle = std::thread::Builder::new()\n            .name(\"hook-epoch-ticker\".into())\n            .spawn(move || {\n                while !stop_clone.load(std::sync::atomic::Ordering::Relaxed) {\n                    std::thread::sleep(Duration::from_millis(100));\n                    ticker_engine.increment_epoch();\n                }\n            })\n            .expect(\"failed to spawn hook epoch ticker\");\n\n        Self {\n            engine,\n            cached_components: Mutex::new(HashMap::new()),\n            config: WasmConfig::default(),\n            kv: None,\n            http_limits: resolve_http_limits(),\n            workspace_root,\n            epoch_stop,\n            epoch_handle: Some(epoch_handle),\n        }\n    }\n\n    /// Set the KV store for hook state persistence.\n    #[must_use]\n    pub(crate) fn with_kv(mut self, kv: ScopedKvStore) -> Self {\n        self.kv = Some(kv);\n        self","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-hooks/src/handler/wasm.rs#L108-L144","documentation":"`WasmHandler::new` spawns a background OS thread that sleeps 100ms at a time and calls `ticker_engine.increment_epoch()` to power epoch-based fuel/interruption of hook execution. It panics if `std::thread::Builder::spawn` fails (thread creation returned an error), because hooks would otherwise never be preempted.","triggerScenarios":"Calling `WasmHandler::new` when `thread::spawn` fails: OS resource exhaustion (hit pid/thread limits like `ulimit -u`, cgroup `pids.max`, or `EAGAIN` from `pthread_create`), or insufficient memory for a new thread stack.","commonSituations":"Running many handlers/threads in constrained containers with low `pids.max`, deep recursion elsewhere exhausting stack, or a process approaching its thread limit under load.","solutions":["Raise the thread/pid limits (`ulimit -u`, cgroup pids.max) in the deployment environment.","Reduce concurrent handler/thread count so spawn succeeds.","Return a `Result` from `new` instead of panicking, mapping the io::Error to a hook-init error.","Consider a lazily started singleton ticker thread shared by all handlers to avoid per-handler spawns."],"exampleFix":"// before\n.spawn(move || { /* epoch loop */ })\n.expect(\"failed to spawn hook epoch ticker\");\n// after\n.spawn(move || { /* epoch loop */ })\n    .map_err(|e| HookError::TickerSpawn(e.to_string()))?","handlingStrategy":"fallback","validationCode":"// Rust: pre-check headroom before spawning the ticker\nfn can_spawn_thread() -> bool {\n    std::thread::Builder::new().stack_size(64 * 1024).spawn(|| {}).map(|h| h.join().is_ok()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"// Rust: fail construction with context instead of panicking\nlet ticker = builder.spawn(loop_body)\n    .map_err(|e| HookError::TickerSpawn(e.to_string()))?;","preventionTips":["Share one epoch ticker thread across handlers instead of one per handler.","Set sane cgroup pids.max / ulimit -u values in deployment manifests.","Log thread counts and alert before hitting the OS thread limit.","Convert thread-spawn failures into typed init errors so callers can degrade gracefully."],"tags":["rust","threads","wasm","resource-exhaustion","panic"],"backgroundTag":"unsupported-platform","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}