abpframework/abp · critical · AbpException

The distributed lock name '{configuration.LockName}' is used

Error message

The distributed lock name '{configuration.LockName}' is used by more than one background job worker (the default worker uses '{WorkerOptions.DistributedLockName}'). Each worker must have a unique lock name to run independently.

What it means

Thrown during BackgroundJobWorkerManager.StartAsync when a dedicated worker's distributed lock name collides with another worker's lock name or with the default worker's lock name (AbpBackgroundJobWorkerOptions.DistributedLockName, default "AbpBackgroundJobWorker"). Each worker needs a unique lock to run independently; a duplicate would cause mutual exclusion to break or serialize the wrong workers. AddDedicatedWorker checks this eagerly, but StartAsync re-validates as a backstop.

Source

Thrown at framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorkerManager.cs:79

        foreach (var configuration in WorkerOptions.WorkerConfigurations)
        {
            var jobNames = configuration.JobArgsTypes
                .Select(GetJobName)
                .Distinct()
                .ToList();

            var alreadyConfigured = jobNames.Intersect(allDedicatedJobNames).ToList();
            if (alreadyConfigured.Any())
            {
                throw new AbpException(
                    $"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.");
            }

            if (lockNames.Contains(configuration.LockName))
            {
                throw new AbpException(
                    $"The distributed lock name '{configuration.LockName}' is used by more than one background job worker " +
                    $"(the default worker uses '{WorkerOptions.DistributedLockName}'). Each worker must have a unique lock name to run independently.");
            }

            lockNames.Add(configuration.LockName);
            allDedicatedJobNames.AddRange(jobNames);
            dedicatedWorkers.Add(new DedicatedWorkerDefinition(configuration.LockName, jobNames));
        }

        foreach (var dedicatedWorker in dedicatedWorkers)
        {
            await StartWorkerAsync(dedicatedWorker.LockName, BackgroundJobNameFilter.Include(dedicatedWorker.JobNames), cancellationToken);
        }

        // Default worker processes every job that is not handled by a dedicated worker.
        await StartWorkerAsync(null, BackgroundJobNameFilter.Exclude(allDedicatedJobNames), cancellationToken);
    }

View on GitHub (pinned to 7ed43b1931)

Solutions

  1. Give each dedicated worker a unique lock name that does not match DistributedLockName (e.g. "AbpBackgroundJobEmailWorker").
  2. Use the parameterless AddDedicatedWorker<TArgs>() overload, which auto-generates a unique lock name from an MD5 hash of the job type names.
  3. If you customized DistributedLockName, ensure no dedicated worker uses the same string.

Example fix

// before — collides with the default lock name
Configure<AbpBackgroundJobWorkerOptions>(opts =>
{
    opts.DistributedLockName = "MyJobWorker";
    opts.AddDedicatedWorker<MyJobArgs>("MyJobWorker"); // collision
});

// after — distinct lock name
Configure<AbpBackgroundJobWorkerOptions>(opts =>
{
    opts.DistributedLockName = "MyDefaultJobWorker";
    opts.AddDedicatedWorker<MyJobArgs>("MyJobDedicatedWorker");
});
Defensive patterns

Strategy: validation

Validate before calling

string lockName = "MyDedicatedWorker";
var workerOpts = serviceProvider.GetRequiredService<IOptions<AbpBackgroundJobWorkerOptions>>().Value;
if (lockName == workerOpts.DistributedLockName ||
    workerOpts.WorkerConfigurations.Any(c => c.LockName == lockName))
{
    throw new InvalidOperationException($"Lock name '{lockName}' is already in use.");
}
options.AddDedicatedWorker(lockName, jobArgsTypes);

Try / catch

try { options.AddDedicatedWorker(lockName, jobArgsTypes); }
catch (AbpException ex) when (ex.Message.Contains("distributed lock name"))
{
    logger.LogError(ex, "Distributed lock name collision for dedicated worker.");
    throw;
}

Prevention

When it happens

Trigger: Calling AddDedicatedWorker with a lockName that equals AbpBackgroundJobWorkerOptions.DistributedLockName (the default worker's lock). Registering two workers with the same explicit lock name. Changing DistributedLockName at runtime after workers are already registered so it now collides with a dedicated worker's name.

Common situations: Explicitly passing "AbpBackgroundJobWorker" as the lock name for a dedicated worker, not realizing it is reserved for the default worker. Copy-pasting a dedicated worker registration line and forgetting to change the lock name. Customizing DistributedLockName globally so it collides with an existing dedicated worker lock.

Related errors


AI-assisted analysis of abpframework/abp@7ed43b1931 (2026-08-13). Data as JSON: /api/errors/a7cc26d063e7171f. Report an issue: GitHub.