HangfireIO/Hangfire · error · InvalidOperationException

Can't perform a background job with a null job.

Error message

Can't perform a background job with a null job.

What it means

Thrown by CoreBackgroundJobPerformer.Perform when the BackgroundJob.Job property is null at execution time. The performer needs a Job to know which method to invoke; a null Job means the background-job record in storage is incomplete or corrupt (no method/type serialized).

Source

Thrown at src/Hangfire.Core/Server/CoreBackgroundJobPerformer.cs:55

        private readonly JobActivator _activator;
        private readonly TaskScheduler _taskScheduler;

        public CoreBackgroundJobPerformer([NotNull] JobActivator activator, [CanBeNull] TaskScheduler taskScheduler)
        {
            _activator = activator ?? throw new ArgumentNullException(nameof(activator));
            _taskScheduler = taskScheduler;
        }

        public object Perform(PerformContext context)
        {
            using (var scope = _activator.BeginScope(context))
            {
                object instance = null;

                if (context.BackgroundJob.Job == null)
                {
                    throw new InvalidOperationException("Can't perform a background job with a null job.");
                }
                
                if (!context.BackgroundJob.Job.Method.IsStatic)
                {
                    instance = scope.Resolve(context.BackgroundJob.Job.Type);

                    if (instance == null)
                    {
                        throw new InvalidOperationException(
                            $"JobActivator returned NULL instance of the '{context.BackgroundJob.Job.Type}' type.");
                    }
                }

                var arguments = SubstituteArguments(context);
                var result = InvokeMethod(context, instance, arguments);

                return result;
            }

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Let the job fail and be moved to the Failed state; then delete it from storage to stop retries.
  2. Investigate how the job was enqueued — ensure BackgroundJob.Enqueue/Job.FromExpression is used so the Job is serialized.
  3. If using a custom storage, verify it round-trips the InvocationData/Job payload correctly.

Example fix

// before — custom enqueuer omitted the method payload
// (job stored without serialized Job)

// after — enqueue through the standard client so Job is serialized
BackgroundJob.Enqueue(() => Console.WriteLine("ok"));
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure jobs are always enqueued with a real method expression so Job is non-null
if (job == null) throw new ArgumentNullException(nameof(job));
BackgroundJob.Enqueue(() => job.Run());

Try / catch

// Let the performer throw; the job moves to Failed state, then purge it
// (no user-side try/catch — handle via dashboard or retry policy)

Prevention

When it happens

Trigger: A worker dequeues a background job whose serialized InvocationData did not yield a Job, or the BackgroundJob object was constructed without a Job. This is reached during actual job performance on the server.

Common situations: Corrupt job record in storage (missing Job fields). A custom enqueuer that created a background job without a proper method expression. Storage migration that lost payload. Race condition where a job was deleted but still being processed.

Related errors


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