HangfireIO/Hangfire · error · ArgumentNullException

task

Error message

task

What it means

Thrown by TaskExtensions.GetTaskLikeResult (internal) when the Task argument is null. The method calls task.GetAwaiter().GetResult() to ensure completion before extracting a result, so a null task would NRE.

Source

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

                {
                    var asTask = type.GetRuntimeMethod("AsTask", EmptyTypes);

                    if (asTask != null && asTask.IsPublic && !asTask.IsStatic &&
                        typeof(Task).GetTypeInfo().IsAssignableFrom(asTask.ReturnType.GetTypeInfo()))
                    {
                        getTaskFunc = obj => (Task) asTask.Invoke(obj, null);
                        return true;
                    }
                }
            }

            getTaskFunc = null;
            return false;
        }

        public static object GetTaskLikeResult([NotNull] this Task task, object obj, Type returnType)
        {
            if (task == null) throw new ArgumentNullException(nameof(task));

            if (task != obj)
            {
                // We shouldn't call GetAwaiter/GetResult on ValueTask directly, because
                // there may be a race condition as tells us this article:
                // https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/#user-content-valid-consumption-patterns-for-valuetasks
                // So we are waiting on task, returned by the AsTask method to ensure it's
                // completed, before querying for the result.
                task.GetAwaiter().GetResult();
            }

            // ReturnType is used instead of task.GetType, because we should return `null` result,
            // when method is returning non-generic Task. However async state machines may use
            // Task<VoidTaskResult> or Task<VoidResult> for these cases, and the number of such
            // void types is pretty high. So it's much safer to call GetAwaiter/GetResult on an
            // original result object.

            // Awaitable type must have a public parameterless GetAwaiter instance method, ...

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Ensure async job methods return a Task (never null) — async methods always do; the issue is sync methods declaring Task return types and returning null.
  2. Audit job methods that declare Task/Task<T> returns for `return null;` or missing return statements.
  3. If wrapping invocation, coalesce null to Task.CompletedTask before this path.

Example fix

// before
public Task RunAsync() { return null; } // job returning null Task

// after
public Task RunAsync() { return DoWorkAsync(); }
// or, for a no-op: public Task RunAsync() => Task.CompletedTask;
Defensive patterns

Strategy: validation

Validate before calling

if (task == null) throw new ArgumentNullException(nameof(task));
return task.GetTaskLikeResult(obj, returnType);

Prevention

When it happens

Trigger: Calling task.GetTaskLikeResult(obj, returnType) where task is null. Reached when Hangfire's invocation pipeline resolves a job's return task to null (e.g., a job method returned null where a Task was expected).

Common situations: An async job method returns null instead of a Task (a coding error in the job); a ValueTask/Task conversion path yielded null; reflection invocation produced a null task object.

Related errors


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