HangfireIO/Hangfire · error · InvalidOperationException

Was unable to initialize a background job '{ctx.BackgroundJo

Error message

Was unable to initialize a background job '{ctx.BackgroundJob.Id}', because it doesn't exist.

What it means

InvalidOperationException thrown inside CoreBackgroundJobFactory.CreateBackgroundJobTwoSteps during a retry attempt (attempt > 0) when Connection.GetJobData(backgroundJob.Id) returns null. On retry, the factory re-checks that the job created by the previous (possibly committed) attempt still exists before applying its initial state. A null result means the job was never persisted or was already expired/removed — the factory cannot initialize state for a non-existent job, so it aborts. This typically surfaces when storage is flaky and the job row disappears between attempts, or when a custom storage's GetJobData implementation is incorrect.

Source

Thrown at src/Hangfire.Core/Client/CoreBackgroundJobFactory.cs:116

                return null;
            }

            var backgroundJob = new BackgroundJob(jobId, context.Job, createdAt, parameters);

            if (context.InitialState != null)
            {
                RetryOnException(ref attemptsLeft, static (attempt, ctx) =>
                {
                    if (attempt > 0)
                    {
                        // Normally, a distributed lock should be applied when making a retry, since
                        // it's possible to get a timeout exception, when transaction was actually
                        // committed. But since background job can't be returned to a position where
                        // its state is null, and since only the current thread knows the job's identifier
                        // when its state is null, and since we shouldn't do anything when it's non-null,
                        // there will be no any race conditions.
                        var data = ctx.Context.Connection.GetJobData(ctx.BackgroundJob.Id);
                        if (data == null) throw new InvalidOperationException($"Was unable to initialize a background job '{ctx.BackgroundJob.Id}', because it doesn't exist.");

                        if (!String.IsNullOrEmpty(data.State)) return;
                    }

                    using (var transaction = ctx.Context.Connection.CreateWriteTransaction())
                    {
                        var applyContext = new ApplyStateContext(
                            ctx.Context.Storage,
                            ctx.Context.Connection,
                            transaction,
                            ctx.BackgroundJob,
                            ctx.Context.InitialState!,
                            oldStateName: null,
                            ctx.Context.Profiler,
                            ctx.StateMachine);

                        ctx.StateMachine.ApplyState(applyContext);

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Set RetryAttempts to 0 (the default) if you do not need creation retries, eliminating the GetJobData re-check path.
  2. Investigate storage health and connectivity — verify GetJobData returns the job immediately after CreateExpiredJob on the same connection.
  3. Ensure no external process (cleanup, expiry, or a competing worker) removes jobs between creation and state initialization.
  4. If using a custom storage, verify GetJobData correctness for just-created job IDs.

Example fix

// before — retries trigger re-check that fails
var factory = new BackgroundJobFactory();
((CoreBackgroundJobFactory)factory._innerFactory).RetryAttempts = 3;

// after — disable creation retries to avoid the path
var factory = new BackgroundJobFactory();
// RetryAttempts stays at default 0
Defensive patterns

Strategy: retry

Validate before calling

// Validate storage connectivity and GetJobData consistency before relying on retries
var data = connection.GetJobData(testJobId);
if (data == null) throw new InvalidOperationException("Storage GetJobData returned null for a known job — investigate before enabling creation retries.");

Try / catch

try { var job = factory.Create(context); }
catch (InvalidOperationException ex) when (ex.Message.Contains("because it doesn't exist"))
{ /* log, check storage health, and re-enqueue from the origin */ }

Prevention

When it happens

Trigger: RetryAttempts > 0 and the first CreateExpiredJob attempt times out (transaction committed server-side but threw to the client); the second attempt's GetJobData returns null because the job expired, was deleted by a worker, or the storage connection points to a different database/node. Also reproducible with a misbehaving IStorageConnection.GetJobData.

Common situations: High-throughput environments where jobs expire or are processed before retry; storage connection failures mid-creation; Redis/SqlServer misconfiguration where GetJobData reads from a replica with replication lag; custom storage implementations that return null for freshly created jobs.

Related errors


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