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

NotSupportedException thrown by CoreBackgroundJobFactory.Create when a job specifies a Queue (context.Job.Queue != null) but the configured JobStorage does not advertise the JobQueueProperty feature (checked via context.Storage.HasFeature(JobStorageFeatures.JobQueueProperty)). Per-job queue targeting via the Job constructor's queue parameter or the queue argument of FromExpression requires storage that understands queue assignment per job; otherwise the [Queue] attribute (a job filter that sets the queue at the state level) is the supported path.

Source

Thrown at src/Hangfire.Core/Client/CoreBackgroundJobFactory.cs:63

        public int RetryAttempts
        {
            get { lock (_syncRoot) { return _retryAttempts; } }
            set { lock (_syncRoot) { _retryAttempts = value; } }
        }

        public Func<int, TimeSpan> RetryDelayFunc
        {
            get { lock (_syncRoot) { return _retryDelayFunc; } }
            set { lock (_syncRoot) { _retryDelayFunc = value; } }
        }

        public BackgroundJob Create(CreateContext context)
        {
            if (context == null) throw new ArgumentNullException(nameof(context));

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

            var parameters = context.Parameters.ToDictionary(
                static x => x.Key,
                static x => SerializationHelper.Serialize(x.Value, SerializationOption.User));

            var createdAt = DateTime.UtcNow;
            var expireIn = TimeSpan.FromDays(30);

            return CreateBackgroundJobTwoSteps(context, parameters, createdAt, expireIn);
        }

        private BackgroundJob CreateBackgroundJobTwoSteps(CreateContext context, Dictionary<string, string> parameters, DateTime createdAt, TimeSpan expireIn)
        {
            var attemptsLeft = Math.Max(RetryAttempts, 0);

            // Retry may cause multiple background jobs to be created, especially when there's
            // a timeout-related exception. But initialization attempt will be performed only

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Use the [Queue("critical")] attribute on the job method/class instead of the queue parameter — it sets the queue through the EnqueuedState and is universally supported.
  2. Switch to a storage that supports per-job queues (e.g., SqlServer, Redis with a current package) which advertises JobQueueProperty.
  3. If you maintain a custom JobStorage, override HasFeature to return true for JobQueueProperty and implement queue-aware job creation.

Example fix

// before — requires JobQueueProperty support
BackgroundJob.Enqueue<MyService>(x => x.DoWork(), "critical");

// after — works with any storage
[Queue("critical")]
public class MyService { public void DoWork() { ... } }
// or on the method:
public class MyService {
    [Queue("critical")]
    public void DoWork() { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

var supportsPerJobQueue = JobStorage.Current.HasFeature(JobStorageFeatures.JobQueueProperty);
if (!supportsPerJobQueue && !string.IsNullOrEmpty(queueName))
    throw new InvalidOperationException("Storage does not support per-job queues; use [Queue] attribute instead.");
// then enqueue normally or via attribute

Type guard

public static bool StorageSupportsPerJobQueue(JobStorage storage)
    => storage != null && storage.HasFeature(JobStorageFeatures.JobQueueProperty);

Try / catch

try { BackgroundJob.Enqueue<T>(x => x.DoWork(), "critical"); }
catch (NotSupportedException ex) when (ex.Message.Contains("queues directly"))
{ /* fall back to [Queue] attribute-based enqueue */ }

Prevention

When it happens

Trigger: Calling Job.FromExpression(() => ..., queue: "critical") or new Job(type, method, args, "critical") against a storage (e.g., MemoryStorage in older versions, or a custom storage) that returns false for HasFeature(JobStorageFeatures.JobQueueProperty). Also triggered by BackgroundJob.Enqueue<T>(..., queue: ...) overloads when storage lacks the feature.

Common situations: Using MemoryStorage or a minimal storage implementation in development that does not implement per-job queue support; upgrading Hangfire and using the new per-job queue API against an older storage package; custom JobStorage implementations that do not declare JobQueueProperty.

Related errors


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