{"record":{"id":"51612f4100e5d0ce","repo":"HangfireIO/Hangfire","slug":"waithandle","errorCode":null,"errorMessage":"waitHandle","messagePattern":"waitHandle","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"src/Hangfire.Core/Processing/TaskExtensions.cs","lineNumber":34,"sourceCode":"using System;\nusing System.Diagnostics;\nusing System.Reflection;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Logging;\n\nnamespace Hangfire.Processing\n{\n    internal static class TaskExtensions\n    {\n        private static readonly Type[] EmptyTypes = Type.EmptyTypes;\n        private static readonly WaitHandle InvalidWaitHandleInstance = new InvalidWaitHandle();\n\n        public static bool WaitOne([NotNull] this WaitHandle waitHandle, TimeSpan timeout, CancellationToken token)\n        {\n            if (waitHandle == null) throw new ArgumentNullException(nameof(waitHandle));\n            if (timeout < Timeout.InfiniteTimeSpan) throw new ArgumentOutOfRangeException(nameof(timeout));\n\n            token.ThrowIfCancellationRequested();\n\n            using var ev = token.GetCancellationEvent();\n\n            var waitHandles = new[] { waitHandle, ev.WaitHandle };\n\n            var stopwatch = Stopwatch.StartNew();\n            var waitResult = WaitHandle.WaitAny(waitHandles, timeout);\n            stopwatch.Stop();\n\n            var timeoutThreshold = TimeSpan.FromMilliseconds(1000);\n            var elapsedThreshold = TimeSpan.FromMilliseconds(500);\n            var protectionTime = TimeSpan.FromSeconds(1);\n\n            if (waitResult == 0)\n            {","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/HangfireIO/Hangfire/blob/c236dd0f930f831ec151e436e138ddc429a02a72/src/Hangfire.Core/Processing/TaskExtensions.cs#L16-L52","documentation":"Thrown by the TaskExtensions.WaitOne extension method (internal) when the WaitHandle argument is null. WaitOne combines the handle with a cancellation event in WaitHandle.WaitAny, so a null handle would NRE inside the array.","triggerScenarios":"Calling waitHandle.WaitOne(timeout, token) where waitHandle is null. Reached through BackgroundExecution's delay handling (_stopped.WaitOne) if the underlying event was null — which should not happen in normal operation.","commonSituations":"A test stub passes a null WaitHandle to exercise the extension; internal state corruption where _stopped was not initialised; a refactor that nulls the handle field prematurely.","solutions":["Ensure the WaitHandle passed is non-null (the ManualResetEvent/CountdownEvent should always be initialised).","Add a null check at the call site and fail fast with context if the handle is unexpectedly null.","If this surfaces in production, treat it as a Hangfire internal-state bug and report it."],"exampleFix":"// before\nhandle.WaitOne(timeout, token); // handle == null\n\n// after\nif (handle == null) throw new InvalidOperationException(\"Wait handle was not initialized.\");\nhandle.WaitOne(timeout, token);","handlingStrategy":"validation","validationCode":"if (waitHandle == null) throw new InvalidOperationException(\"WaitHandle was not initialized.\");\nreturn waitHandle.WaitOne(timeout, token);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the owning event (ManualResetEvent/CountdownEvent) is constructed before any wait.","Do not null out handle fields before all waits complete.","Treat an unexpected null here as an internal-state defect."],"tags":["hangfire","null-argument","argument-validation","threading","background-processing"],"backgroundTag":null,"analyzedSha":"c236dd0f930f831ec151e436e138ddc429a02a72","analyzedAt":"2026-08-13T20:27:11.027Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}