HangfireIO/Hangfire · error · ArgumentNullException

waitHandle

Error message

waitHandle

What it means

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.

Source

Thrown at src/Hangfire.Core/Processing/TaskExtensions.cs:34

using System;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Hangfire.Annotations;
using Hangfire.Common;
using Hangfire.Logging;

namespace Hangfire.Processing
{
    internal static class TaskExtensions
    {
        private static readonly Type[] EmptyTypes = Type.EmptyTypes;
        private static readonly WaitHandle InvalidWaitHandleInstance = new InvalidWaitHandle();

        public static bool WaitOne([NotNull] this WaitHandle waitHandle, TimeSpan timeout, CancellationToken token)
        {
            if (waitHandle == null) throw new ArgumentNullException(nameof(waitHandle));
            if (timeout < Timeout.InfiniteTimeSpan) throw new ArgumentOutOfRangeException(nameof(timeout));

            token.ThrowIfCancellationRequested();

            using var ev = token.GetCancellationEvent();

            var waitHandles = new[] { waitHandle, ev.WaitHandle };

            var stopwatch = Stopwatch.StartNew();
            var waitResult = WaitHandle.WaitAny(waitHandles, timeout);
            stopwatch.Stop();

            var timeoutThreshold = TimeSpan.FromMilliseconds(1000);
            var elapsedThreshold = TimeSpan.FromMilliseconds(500);
            var protectionTime = TimeSpan.FromSeconds(1);

            if (waitResult == 0)
            {

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Ensure the WaitHandle passed is non-null (the ManualResetEvent/CountdownEvent should always be initialised).
  2. Add a null check at the call site and fail fast with context if the handle is unexpectedly null.
  3. If this surfaces in production, treat it as a Hangfire internal-state bug and report it.

Example fix

// before
handle.WaitOne(timeout, token); // handle == null

// after
if (handle == null) throw new InvalidOperationException("Wait handle was not initialized.");
handle.WaitOne(timeout, token);
Defensive patterns

Strategy: validation

Validate before calling

if (waitHandle == null) throw new InvalidOperationException("WaitHandle was not initialized.");
return waitHandle.WaitOne(timeout, token);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13). Data as JSON: /api/errors/51612f4100e5d0ce. Report an issue: GitHub.