HangfireIO/Hangfire · error · NotSupportedException

Only public methods can be invoked in the background…

Error message

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.

What it means

NotSupportedException thrown by Job.Validate when the target method is not public. Hangfire invokes job methods by reflection in a separate (possibly different) process, so non-public methods (private, protected, internal) and explicit interface implementations are rejected at job-definition time because they cannot be reliably invoked across process boundaries and serialized metadata. The error message explicitly calls out explicit interface implementation as a common culprit (the generated method is private).

Solutions

  1. Change the method's access modifier to public.
  2. If the method is an explicit interface implementation, enqueue against the interface using BackgroundJob.Enqueue<IFoo>(x => x.Bar()).
  3. Move the job method to a public service class if the current type cannot be made public.

Example fix

// before — private / explicit interface impl
public class Worker {
    void IJob.Run() { ... }      // explicit impl → private backing method
    private void DoRun() { ... }
}
BackgroundJob.Enqueue<Worker>(x => ((IJob)x).Run());

// after — public method, or enqueue against the interface
public class Worker : IJob {
    public void Run() { ... }    // public
}
BackgroundJob.Enqueue<IJob>(x => x.Run());
Defensive patterns

Strategy: validation

Validate before calling

if (!method.IsPublic)
    throw new NotSupportedException($"Method {method.Name} must be public to be used as a job method.");
// or check at design time: ensure the job method has the 'public' modifier.

Type guard

public static bool IsPublicJobMethod(MethodInfo m) => m != null && m.IsPublic;

Try / catch

try { BackgroundJob.Enqueue<T>(x => x.Work()); }
catch (NotSupportedException ex) when (ex.Message.Contains("public methods")) { /* make the method public or enqueue the interface */ }

Prevention

When it happens

Trigger: Enqueuing () => obj.PrivateMethod(); a method marked internal, protected, or private; a method that is an explicit interface implementation (void IFoo.Bar()) whose backing method is private; a method on an internal type.

Common situations: Refactoring a public method to private; using explicit interface implementation and enqueuing the concrete type; enqueuing a method defined on an internal class; F# or VB code where default accessibility differs from C#.

Related errors


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

Appendix: source

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

                type,
                method,
                GetExpressionValues(callExpression.Arguments),
                queue);
        }

        private static void Validate(
            Type type, 
            [InvokerParameterName] string typeParameterName,
            MethodInfo method, 
            // 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);
            }

View on GitHub (pinned to c236dd0f93)