{"record":{"id":"c216fb5f33c2b63d","repo":"HangfireIO/Hangfire","slug":"state-change-of-a-background-job-failed-see-inner","errorCode":null,"errorMessage":"State change of a background job failed. See inner exception for details","messagePattern":"State change of a background job failed\\. See inner exception for details","errorType":"exception","errorClass":"BackgroundJobClientException","httpStatus":null,"severity":"error","filePath":"src/Hangfire.Core/BackgroundJobClient.cs","lineNumber":189,"sourceCode":"            if (state == null) throw new ArgumentNullException(nameof(state));\n\n            try\n            {\n                using (var connection = _storage.GetConnection())\n                {\n                    var appliedState = _stateChanger.ChangeState(new StateChangeContext(\n                        _storage,\n                        connection,\n                        jobId,\n                        state,\n                        expectedState != null ? new[] { expectedState } : null));\n\n                    return appliedState != null && appliedState.Name.Equals(state.Name, StringComparison.OrdinalIgnoreCase);\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new BackgroundJobClientException(\"State change of a background job failed. See inner exception for details\", ex);\n            }\n        }\n    }\n}\n","sourceCodeStart":171,"sourceCodeEnd":194,"githubUrl":"https://github.com/HangfireIO/Hangfire/blob/c236dd0f930f831ec151e436e138ddc429a02a72/src/Hangfire.Core/BackgroundJobClient.cs#L171-L194","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nclient.Delete(jobId);\n\n// after\ntry\n{\n    client.Delete(jobId);\n}\ncatch (BackgroundJobClientException ex)\n{\n    logger.Error(ex.InnerException, \"State change failed\");\n}","handlingStrategy":"try-catch","validationCode":"if (string.IsNullOrEmpty(jobId) || state == null) throw new ArgumentException(\"Invalid args\");","typeGuard":null,"tryCatchPattern":"try\n{\n    var ok = client.ChangeState(jobId, state, expectedState);\n}\ncatch (BackgroundJobClientException ex)\n{\n    logger.Error(ex.InnerException, \"State change failed for {JobId}\", jobId);\n}","preventionTips":["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."],"tags":["storage","state-change","backgroundjobclientexception","wrapper"],"backgroundTag":null,"analyzedSha":"c236dd0f930f831ec151e436e138ddc429a02a72","analyzedAt":"2026-08-13T20:27:11.027Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}