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
- Move the job method into a static or instance class so it has a concrete DeclaringType.
- For F#, place functions in a type (a member) rather than at module level.
- 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
- Define job methods on static or instance classes, never at module/function top level.
- For F#, place job functions inside a type member.
- Never enqueue DynamicMethod or Reflection.Emit-generated methods.
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
- Only public methods can be invoked in the background. Ensure
- Job method can not contain unassigned generic type parameter
- The type `{method.DeclaringType}` must be derived from the `
- Async void methods are not supported. Use async Task instead
- Argument count must be equal to method parameter count.
AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13).
Data as JSON: /api/errors/e84dd195ed7772cb.
Report an issue: GitHub.