HangfireIO/Hangfire · error · NotSupportedException

Global methods are not supported. Use class methods instead.

Error message

Global methods are not supported. Use class methods instead.

What it means

NotSupportedException thrown by Job.Validate when method.DeclaringType is null, which occurs for top-level (global) functions in some languages or for dynamically emitted methods that have no declaring type. Hangfire requires job methods to belong to a type so it can serialize the type name and locate it by reflection when performing the job elsewhere. Methods without a declaring type cannot be located reliably across process boundaries.

Source

Thrown at src/Hangfire.Core/Common/Job.cs:487

            // ReSharper disable once UnusedParameter.Local
            [InvokerParameterName] string methodParameterName,
            // ReSharper disable once UnusedParameter.Local
            int argumentCount,
            [InvokerParameterName] string argumentParameterName)
        {
            if (!method.IsPublic)
            {
                throw new NotSupportedException("Only public methods can be invoked in the background. Ensure your method has the `public` access modifier, and you aren't using explicit interface implementation.");
            }

            if (method.ContainsGenericParameters)
            {
                throw new NotSupportedException("Job method can not contain unassigned generic type parameters.");
            }

            if (method.DeclaringType == null)
            {
                throw new NotSupportedException("Global methods are not supported. Use class methods instead.");
            }

            if (!method.DeclaringType.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
            {
                throw new ArgumentException(
                    $"The type `{method.DeclaringType}` must be derived from the `{type}` type.",
                    typeParameterName);
            }

            if (method.ReturnType == typeof(void) &&
                AsyncStateMachineAttributeCache.GetOrAdd(method, static m => m.GetCustomAttribute<AsyncStateMachineAttribute>()) != null)
            {
                throw new NotSupportedException("Async void methods are not supported. Use async Task instead.");
            }

            var parameters = method.GetParameters();

            if (parameters.Length != argumentCount)

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Move the job method into a static or instance class so it has a concrete DeclaringType.
  2. For F#, place functions in a type (a member) rather than at module level.
  3. Avoid enqueuing DynamicMethod or Reflection.Emit methods — define a real method on a real type.

Example fix

// before — F# module-level function / global-style
let doWork () = ... // module function, may have no usable declaring type
BackgroundJob.Enqueue(() -> doWork())

// after — wrap in a type
type Worker() =
    member _.DoWork() = ...
BackgroundJob.Enqueue<Worker>(fun x -> x.DoWork())
Defensive patterns

Strategy: validation

Validate before calling

if (method.DeclaringType == null)
    throw new NotSupportedException("Job method has no declaring type; move it into a class.");

Type guard

public static bool HasDeclaringType(MethodInfo m) => m?.DeclaringType != null;

Prevention

When it happens

Trigger: Enqueuing a local function (C#) whose DeclaringType may be a compiler-generated type (rarely null but can be exotic); F# module-level functions; dynamically generated methods via DynamicMethod or Reflection.Emit with no declaring type; some interop scenarios.

Common situations: Using F# top-level functions as job methods; enqueuing a DynamicMethod; reflection-based construction where MethodInfo comes from an unsupported source; edge cases with compiler-generated closure classes.

Related errors


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