{"record":{"id":"7cbeba4c07749f8f","repo":"abpframework/abp","slug":"cannot-access-a-disposed-object","errorCode":null,"errorMessage":"Cannot access a disposed object.","messagePattern":"Cannot access a disposed object\\.","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/DefaultDynamicBackgroundWorkerManager.cs","lineNumber":57,"sourceCode":"        Check.NotNullOrWhiteSpace(workerName, nameof(workerName));\n        Check.NotNull(schedule, nameof(schedule));\n        Check.NotNull(handler, nameof(handler));\n\n        schedule.Validate();\n\n        if (!schedule.CronExpression.IsNullOrWhiteSpace())\n        {\n            throw new AbpException(\n                $\"The default in-memory background worker manager does not support CronExpression for dynamic worker '{workerName}'. \" +\n                \"Please clear CronExpression and use Period-based scheduling, or use a scheduler-backed provider (Hangfire or Quartz).\");\n        }\n\n        await _semaphore.WaitAsync(cancellationToken);\n        try\n        {\n            if (_isDisposed)\n            {\n                throw new ObjectDisposedException(nameof(DefaultDynamicBackgroundWorkerManager));\n            }\n\n            if (_dynamicWorkers.TryRemove(workerName, out var existingWorker))\n            {\n                await existingWorker.StopAsync(cancellationToken);\n                Logger.LogInformation(\"Replaced existing dynamic worker: {WorkerName}\", workerName);\n            }\n\n            var worker = CreateDynamicWorker(workerName, schedule, handler);\n            _dynamicWorkers[workerName] = worker;\n\n            await worker.StartAsync(cancellationToken);\n        }\n        finally\n        {\n            _semaphore.Release();\n        }\n    }","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/abpframework/abp/blob/7ed43b1931b9df46a50c0c59148a18645641d0df/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/DefaultDynamicBackgroundWorkerManager.cs#L39-L75","documentation":"Thrown by DefaultDynamicBackgroundWorkerManager.AddAsync as an ObjectDisposedException when attempting to add a dynamic worker after the manager has been disposed (StopAllAsync was called, setting _isDisposed = true). The manager is a singleton that tears down all dynamic workers on shutdown; any add call after that is invalid.","triggerScenarios":"Calling IDynamicBackgroundWorkerManager.AddAsync during or after application shutdown (StopAllAsync has run). A background service or hosted service that tries to register a dynamic worker while the host is stopping, racing with the manager's disposal.","commonSituations":"A hosted service's StopAsync triggers a dynamic worker registration as part of shutdown logic. A long-running operation that outlives host shutdown tries to add a worker. Unit/integration tests that call StopAllAsync and then continue using the same manager instance.","solutions":["Check IHostApplicationLifetime.ApplicationStopping before calling AddAsync, and skip registration if the application is shutting down.","Ensure dynamic worker registration happens during startup or steady-state, not during shutdown.","In tests, create a fresh manager (or a fresh test host) rather than reusing a disposed one."],"exampleFix":"// before — may race with shutdown\npublic class MyHostedService : IHostedService\n{\n    public async Task StartAsync(CancellationToken ct)\n    {\n        await _dynamicManager.AddAsync(\"worker\", schedule, handler, ct);\n    }\n}\n\n// after — guard against shutdown\npublic async Task StartAsync(CancellationToken ct)\n{\n    if (_lifetime.ApplicationStopping.IsCancellationRequested) return;\n    await _dynamicManager.AddAsync(\"worker\", schedule, handler, ct);\n}","handlingStrategy":"validation","validationCode":"if (_lifetime.ApplicationStopping.IsCancellationRequested) return;\nawait dynamicManager.AddAsync(name, schedule, handler, ct);","typeGuard":"static bool IsSafeToAdd(IHostApplicationLifetime lifetime) => !lifetime.ApplicationStopping.IsCancellationRequested;","tryCatchPattern":"try { await dynamicManager.AddAsync(name, schedule, handler, ct); }\ncatch (ObjectDisposedException) { logger.LogWarning(\"Dynamic worker manager disposed; skipping AddAsync during shutdown.\"); }","preventionTips":["Check IHostApplicationLifetime.ApplicationStopping before dynamic registration calls.","Register dynamic workers during startup, not during shutdown or teardown.","In tests, use a fresh manager/host per test to avoid cross-test disposal races."],"tags":["background-workers","dynamic-workers","object-disposed","lifecycle","race-condition"],"backgroundTag":null,"analyzedSha":"7ed43b1931b9df46a50c0c59148a18645641d0df","analyzedAt":"2026-08-13T16:26:11.351Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}