{"record":{"id":"ef58de96c36cddf2","repo":"napi-rs/napi-rs","slug":"async-runtime-generation-task-id-space-exhausted","errorCode":null,"errorMessage":"async runtime generation task id space exhausted","messagePattern":"async runtime generation task id space exhausted","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/async-runtime/src/async_runtime.rs","lineNumber":2658,"sourceCode":"        abort_handles: FxHashMap::default(),\n      }),\n      idle: Condvar::new(),\n    })\n  }\n\n  fn try_register_async(self: &Arc<Self>) -> Option<(AbortRegistration, GenerationWorkGuard)> {\n    let (abort_handle, abort_registration) = AbortHandle::new_pair();\n    let mut state = self\n      .state\n      .lock()\n      .unwrap_or_else(std::sync::PoisonError::into_inner);\n    if state.closed {\n      return None;\n    }\n    let task_id = state.next_task_id;\n    let Some(next_task_id) = state.next_task_id.checked_add(1) else {\n      drop(state);\n      panic!(\"async runtime generation task id space exhausted\");\n    };\n    state.next_task_id = next_task_id;\n    state.active += 1;\n    state.abort_handles.insert(task_id, abort_handle);\n    Some((\n      abort_registration,\n      GenerationWorkGuard {\n        work: Arc::clone(self),\n        task_id: Some(task_id),\n      },\n    ))\n  }\n\n  fn try_register_work(self: &Arc<Self>) -> Option<GenerationWorkGuard> {\n    let mut state = self\n      .state\n      .lock()\n      .unwrap_or_else(std::sync::PoisonError::into_inner);","sourceCodeStart":2640,"sourceCodeEnd":2676,"githubUrl":"https://github.com/napi-rs/napi-rs/blob/39bd1205e480a453a2da2601a760bde5a71ed016/crates/async-runtime/src/async_runtime.rs#L2640-L2676","documentation":"The runtime allocates per-task ids from a monotonically increasing `next_task_id` within the current generation. When spawning a task would overflow the id counter, the runtime panics (after dropping the state guard) rather than reusing ids, since abort-handle bookkeeping depends on unique ids. Effectively unreachable defensive code.","triggerScenarios":"Spawning a task (via the runtime's task-spawn path that inserts into `abort_handles`) when `next_task_id` has reached `u64::MAX` within one runtime generation — i.e. ~2^64 spawns without resetting the runtime generation.","commonSituations":"Never in normal apps; only fuzzing, synthetic tests saturating the counter, or extremely long-lived runtimes with astronomically many spawns.","solutions":["Restart the process or recreate the runtime to start a new generation.","In tests, reduce spawn counts or reset the generation counter.","Report upstream if hit — a real hit indicates a generation-rotation bug."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Not guardable; requires ~2^64 spawns in one runtime generation.\n// Recreate the runtime long before id exhaustion.","typeGuard":null,"tryCatchPattern":"std::panic::catch_unwind(|| runtime.spawn(task)).unwrap_or_else(|_| recreate_runtime());","preventionTips":["Periodically recreate the runtime in extremely long-lived processes","Never inject u64::MAX into next_task_id in tests","Report any real occurrence upstream"],"tags":["async-runtime","task-spawn","overflow","panic"],"backgroundTag":"value-out-of-range","analyzedSha":"39bd1205e480a453a2da2601a760bde5a71ed016","analyzedAt":"2026-09-13T20:31:47.814Z","contentChangedAt":"2026-09-13T20:31:47.814Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}