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
- Inspect InnerException to find the true cause (connection, filter, state logic).
- For storage errors: confirm connectivity and connection string; check for long-running locks.
- For filter errors: review any global or job-level state-handling filters (e.g., IApplyStateFilter).
- 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
- Inspect InnerException for the underlying storage/filter error.
- Validate jobId and state are non-null before calling.
- Review custom state filters (IApplyStateFilter) that may throw.
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
- Background job creation failed. See inner exception for deta
- storage
- Was unable to initialize a background job '{ctx.BackgroundJo
- configuration
- jobStorage
AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13).
Data as JSON: /api/errors/c216fb5f33c2b63d.
Report an issue: GitHub.