HangfireIO/Hangfire · error · BackgroundJobClientException

State change of a background job failed. See inner exception

Error message

State change of a background job failed. See inner exception for details

What it means

BackgroundJobClient.ChangeState wraps any catchable exception thrown during a state transition — opening a storage connection or invoking the IBackgroundJobStateChanger — into a BackgroundJobClientException with the original error as InnerException. Unlike Create, this fires on Delete/Requeue/Reschedule/ChangeState operations that change a job's state. The actual root cause is always in InnerException.

Source

Thrown at src/Hangfire.Core/BackgroundJobClient.cs:189

            if (state == null) throw new ArgumentNullException(nameof(state));

            try
            {
                using (var connection = _storage.GetConnection())
                {
                    var appliedState = _stateChanger.ChangeState(new StateChangeContext(
                        _storage,
                        connection,
                        jobId,
                        state,
                        expectedState != null ? new[] { expectedState } : null));

                    return appliedState != null && appliedState.Name.Equals(state.Name, StringComparison.OrdinalIgnoreCase);
                }
            }
            catch (Exception ex) when (ex.IsCatchableExceptionType())
            {
                throw new BackgroundJobClientException("State change of a background job failed. See inner exception for details", ex);
            }
        }
    }
}

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Inspect InnerException to find the true cause (connection, filter, state logic).
  2. For storage errors: confirm connectivity and connection string; check for long-running locks.
  3. For filter errors: review any global or job-level state-handling filters (e.g., IApplyStateFilter).
  4. Wrap ChangeState/Requeue/Delete/Reschedule calls in try/catch(BackgroundJobClientException).

Example fix

// before
client.Delete(jobId);

// after
try
{
    client.Delete(jobId);
}
catch (BackgroundJobClientException ex)
{
    logger.Error(ex.InnerException, "State change failed");
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (string.IsNullOrEmpty(jobId) || state == null) throw new ArgumentException("Invalid args");

Try / catch

try
{
    var ok = client.ChangeState(jobId, state, expectedState);
}
catch (BackgroundJobClientException ex)
{
    logger.Error(ex.InnerException, "State change failed for {JobId}", jobId);
}

Prevention

When it happens

Trigger: Calling client.ChangeState(jobId, state, expectedState) (directly or via Delete/Requeue/Reschedule) when the storage connection fails, the IBackgroundJobStateChanger throws, a state-transition filter throws, or the jobId references a job whose state data is corrupt.

Common situations: Storage backend down or unreachable during a delete/requeue; a custom IState or state filter throwing; transactional state change failing due to locking or schema issues.

Related errors


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