HangfireIO/Hangfire · error · NotSupportedException

Current storage doesn't support specifying queues directly f

Error message

Current storage doesn't support specifying queues directly for a specific job. Please use the QueueAttribute instead.

What it means

Thrown by RecurringJobManager.AddOrUpdate when a Job has a Queue set but the configured JobStorage does not advertise the JobQueueProperty feature (JobStorageFeatures.JobQueueProperty = "Job.Queue"). Per-job queue targeting is an opt-in storage capability; storages that don't support it must use the QueueAttribute or default queue instead.

Source

Thrown at src/Hangfire.Core/RecurringJobManager.cs:108

            _factory = factory ?? throw new ArgumentNullException(nameof(factory));
            _timeZoneResolver = timeZoneResolver ?? throw new ArgumentNullException(nameof(timeZoneResolver));
            _nowFactory = nowFactory ?? throw new ArgumentNullException(nameof(nowFactory));
        }

        public JobStorage Storage => _storage;

        public void AddOrUpdate(string recurringJobId, Job job, string cronExpression, RecurringJobOptions options)
        {
            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));
            if (job == null) throw new ArgumentNullException(nameof(job));
            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));
            if (options == null) throw new ArgumentNullException(nameof(options));

            ValidateCronExpression(cronExpression);

            if (job.Queue != null && !Storage.HasFeature(JobStorageFeatures.JobQueueProperty))
            {
                throw new NotSupportedException("Current storage doesn't support specifying queues directly for a specific job. Please use the QueueAttribute instead.");
            }

            using (var connection = _storage.GetConnection())
            using (connection.AcquireDistributedRecurringJobLock(recurringJobId, DefaultTimeout))
            {
                var now = _nowFactory();
                var recurringJob = connection.GetOrCreateRecurringJob(recurringJobId);
                var scheduleChanged = false;

                recurringJob.Job = InvocationData.SerializeJob(job).SerializePayload();

                if (!cronExpression.Equals(recurringJob.Cron, StringComparison.OrdinalIgnoreCase))
                {
                    recurringJob.Cron = cronExpression;
                    scheduleChanged = true;
                }

                if (!options.TimeZone.Id.Equals(recurringJob.TimeZoneId, StringComparison.OrdinalIgnoreCase))

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Remove the per-job Queue / [Queue] attribute so the job uses the default queue on this storage.
  2. Switch to a storage that supports JobQueueProperty (e.g. SqlServer, Redis) when per-job queue routing is required.
  3. If building a custom storage, implement HasFeature to return true for JobStorageFeatures.JobQueueProperty and support the queue on enqueue.

Example fix

// before
[Queue("critical")]
public static void Work() { }
RecurringJob.AddOrUpdate("j", () => Work(), Cron.Daily()); // unsupported storage

// after — remove per-job queue on unsupported storage
public static void Work() { }
RecurringJob.AddOrUpdate("j", () => Work(), Cron.Daily());
Defensive patterns

Strategy: validation

Validate before calling

if (job.Queue != null && !JobStorage.Current.HasFeature(JobStorageFeatures.JobQueueProperty))
{
    throw new InvalidOperationException("This storage cannot route per-job queues; remove the Queue attribute.");
}

Prevention

When it happens

Trigger: Creating a Job with a non-null Queue (via Job.FromExpression with QueueAttribute, or by setting Job.Queue) and calling AddOrUpdate against a storage that doesn't report the Job.Queue feature — typically the in-memory storage or a minimal custom storage.

Common situations: Using [Queue("critical")] on a method and registering it as a recurring job while backed by a storage without queue-property support (e.g. MemoryStorage in older versions, or a custom storage). Mixing a queue-aware job definition with a storage that only supports the default queue.

Related errors


AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13). Data as JSON: /api/errors/2ca78f268844694e. Report an issue: GitHub.