{"record":{"id":"cf7bad319afc4d71","repo":"HangfireIO/Hangfire","slug":"job-has-been-performed-but-an-exception-occurred","errorCode":null,"errorMessage":"Job has been performed, but an exception occurred during disposal.","messagePattern":"Job has been performed, but an exception occurred during disposal\\.","errorType":"exception","errorClass":"JobPerformanceException","httpStatus":null,"severity":"error","filePath":"src/Hangfire.Core/Obsolete/Job.Obsolete.cs","lineNumber":158,"sourceCode":"            }\n            catch (TargetInvocationException ex)\n            {\n                CoreBackgroundJobPerformer.HandleJobPerformanceException(ex.InnerException, cancellationToken, null);\n                throw;\n            }\n        }\n\n        [Obsolete(\"Will be removed in 2.0.0\")]\n        private static void Dispose(object instance)\n        {\n            try\n            {\n                var disposable = instance as IDisposable;\n                disposable?.Dispose();\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new JobPerformanceException(\n                    \"Job has been performed, but an exception occurred during disposal.\",\n                    ex);\n            }\n        }\n    }\n}","sourceCodeStart":140,"sourceCodeEnd":164,"githubUrl":"https://github.com/HangfireIO/Hangfire/blob/c236dd0f930f831ec151e436e138ddc429a02a72/src/Hangfire.Core/Obsolete/Job.Obsolete.cs#L140-L164","documentation":"Thrown as JobPerformanceException (message 'Job has been performed, but an exception occurred during disposal.') wrapping any catchable exception raised while disposing the job instance after successful invocation. The obsolete Dispose() static method (lines 148-162) casts the instance to IDisposable and calls Dispose() inside a try/catch, so a failing Dispose still surfaces as a performance error even though the job logic itself completed.","triggerScenarios":"Job.Perform (obsolete) finishes invoking the method, then in its 'finally' block calls Dispose(instance). The instance implements IDisposable and its Dispose() throws — e.g. flushing a buffer to a closed connection, closing an already-disposed resource, or releasing a native handle that has been invalidated.","commonSituations":"The job class's Dispose() closes a database connection or file handle that was already disposed by the job method; Dispose() flushes to a network stream whose underlying socket has been torn down; a third-party dependency's Dispose throws on shutdown. Note the job result was produced successfully — only cleanup failed.","solutions":["Inspect the InnerException to find the disposal failure and make the job's Dispose() idempotent and exception-safe (guard against double-dispose, swallow non-catal cleanup errors).","Separate transactional work from disposal: commit/persist the job's result before Dispose runs, so a disposal exception cannot imply a lost job.","Consider moving disposable resource lifetimes into the job method body with 'using' rather than implementing IDisposable on the job class itself."],"exampleFix":"// before — Dispose throws on already-closed connection\npublic void Dispose() { _connection.Close(); }\n\n// after — idempotent, exception-safe disposal\nprivate bool _disposed;\npublic void Dispose()\n{\n    if (_disposed) return;\n    try { _connection.Close(); } finally { _disposed = true; }\n}","handlingStrategy":"try-catch","validationCode":"// Make the job's Dispose idempotent before performing so cleanup can't throw:\n// (see exampleFix) — there is no pre-check that predicts a disposal failure.","typeGuard":null,"tryCatchPattern":"try { job.Perform(activator, token); }\ncatch (JobPerformanceException ex) when (ex.Message.Contains(\"during disposal\"))\n{\n    logger.LogWarning(ex.InnerException, \"Job succeeded but disposal failed.\");\n}","preventionTips":["Make IDisposable.Dispose on job classes idempotent and exception-safe.","Move disposable resource lifetimes into the job method (using) rather than the class.","Persist results before Dispose runs."],"tags":["job-performance","disposal","idisposable","obsolete","exception-wrapping"],"backgroundTag":null,"analyzedSha":"c236dd0f930f831ec151e436e138ddc429a02a72","analyzedAt":"2026-08-13T20:27:11.027Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}