{"record":{"id":"be3ea6fd73409526","repo":"abpframework/abp","slug":"the-following-background-job-s-are-configured-for","errorCode":null,"errorMessage":"The following background job(s) are configured for more than one dedicated worker: {string.Join(\", \", alreadyConfigured)}. Each job type can be handled by only one dedicated worker.","messagePattern":"The following background job\\(s\\) are configured for more than one dedicated worker: (.+?)\\. Each job type can be handled by only one dedicated worker\\.","errorType":"exception","errorClass":"AbpException","httpStatus":null,"severity":"critical","filePath":"framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorkerManager.cs","lineNumber":72,"sourceCode":"        // for what can only be known here: two different args types that resolve to the same job name.\n        // Validate all configurations first, so a misconfiguration does not leave already-started workers running.\n        var dedicatedWorkers = new List<DedicatedWorkerDefinition>();\n        var allDedicatedJobNames = new List<string>();\n\n        // The default worker uses WorkerOptions.DistributedLockName, so dedicated workers must not reuse it.\n        var lockNames = new List<string> { WorkerOptions.DistributedLockName };\n\n        foreach (var configuration in WorkerOptions.WorkerConfigurations)\n        {\n            var jobNames = configuration.JobArgsTypes\n                .Select(GetJobName)\n                .Distinct()\n                .ToList();\n\n            var alreadyConfigured = jobNames.Intersect(allDedicatedJobNames).ToList();\n            if (alreadyConfigured.Any())\n            {\n                throw new AbpException(\n                    $\"The following background job(s) are configured for more than one dedicated worker: {string.Join(\", \", alreadyConfigured)}. \" +\n                    $\"Each job type can be handled by only one dedicated worker.\");\n            }\n\n            if (lockNames.Contains(configuration.LockName))\n            {\n                throw new AbpException(\n                    $\"The distributed lock name '{configuration.LockName}' is used by more than one background job worker \" +\n                    $\"(the default worker uses '{WorkerOptions.DistributedLockName}'). Each worker must have a unique lock name to run independently.\");\n            }\n\n            lockNames.Add(configuration.LockName);\n            allDedicatedJobNames.AddRange(jobNames);\n            dedicatedWorkers.Add(new DedicatedWorkerDefinition(configuration.LockName, jobNames));\n        }\n\n        foreach (var dedicatedWorker in dedicatedWorkers)\n        {","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/abpframework/abp/blob/7ed43b1931b9df46a50c0c59148a18645641d0df/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorkerManager.cs#L54-L90","documentation":"Thrown during BackgroundJobWorkerManager.StartAsync when two dedicated worker configurations resolve to overlapping job names. This is a backstop: AddDedicatedWorker already rejects duplicate job *types* eagerly at registration time, but two distinct args types can share the same resolved job name (via BackgroundJobNameAttribute or JobOptions configuration). This check prevents two workers from competing for the same job at runtime.","triggerScenarios":"Registering two dedicated workers where the args types are different but both resolve to the same JobName through BackgroundJobNameAttribute.GetNameOrNull or the AbpBackgroundJobOptions.GetJob mapping. For example, SharedNameJobAArgs and SharedNameJobBArgs both decorated with [BackgroundJobName(\"shared-job\")] and assigned to separate dedicated workers.","commonSituations":"Two developers independently add [BackgroundJobName(\"email\")] to different args types in separate modules, then each module registers its own dedicated worker. Refactoring a job's args type into a new type while forgetting to remove the old dedicated worker registration. Sharing a job name string across two modules via a constant.","solutions":["Inspect the error's listed job names, find which args types share each name (check [BackgroundJobName] attributes and AddJob/JobName configuration), and ensure each job name maps to exactly one dedicated worker.","Merge the two dedicated worker configurations into one by passing both args types to a single AddDedicatedWorker call.","Give each args type a distinct job name by removing the conflicting [BackgroundJobName] attribute or registering unique names in AbpBackgroundJobOptions."],"exampleFix":"// before — two types, same job name, two workers\ncontext.Services.Configure<AbpBackgroundJobWorkerOptions>(opts =>\n{\n    opts.AddDedicatedWorker<SharedNameJobAArgs>(\"lock-a\");\n    opts.AddDedicatedWorker<SharedNameJobBArgs>(\"lock-b\"); // both resolve to \"shared-job\"\n});\n\n// after — both types handled by one dedicated worker\ncontext.Services.Configure<AbpBackgroundJobWorkerOptions>(opts =>\n{\n    opts.AddDedicatedWorker<SharedNameJobAArgs, SharedNameJobBArgs>(\"lock-a\");\n});","handlingStrategy":"validation","validationCode":"// Before StartAsync, verify no two dedicated workers share a resolved job name.\nvar jobOptions = serviceProvider.GetRequiredService<IOptions<AbpBackgroundJobOptions>>().Value;\nvar workerOptions = serviceProvider.GetRequiredService<IOptions<AbpBackgroundJobWorkerOptions>>().Value;\nvar seenNames = new HashSet<string>();\nforeach (var cfg in workerOptions.WorkerConfigurations)\n{\n    foreach (var t in cfg.JobArgsTypes)\n    {\n        var name = jobOptions.GetJob(t).JobName;\n        if (!seenNames.Add(name))\n            throw new InvalidOperationException($\"Job name '{name}' is assigned to more than one dedicated worker.\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try { await backgroundJobWorkerManager.StartAsync(cancellationToken); }\ncatch (AbpException ex) when (ex.Message.Contains(\"configured for more than one dedicated worker\"))\n{\n    logger.LogError(ex, \"Duplicate dedicated worker job assignment; check AddDedicatedWorker registrations.\");\n    throw;\n}","preventionTips":["Avoid sharing [BackgroundJobName] values across different args types that each get a dedicated worker.","Consolidate job types that must run on the same worker into a single AddDedicatedWorker call.","Add a startup health check that resolves job names for all WorkerConfigurations and asserts uniqueness."],"tags":["background-jobs","dedicated-worker","configuration-conflict","startup"],"backgroundTag":null,"analyzedSha":"7ed43b1931b9df46a50c0c59148a18645641d0df","analyzedAt":"2026-08-13T16:26:11.351Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}