HangfireIO/Hangfire · error · ArgumentException
Expression body should be of type `MethodCallExpression`
Error message
Expression body should be of type `MethodCallExpression`
What it means
ArgumentException thrown by Job.FromExpression when the supplied lambda's Body is not a MethodCallExpression. Hangfire builds a Job by extracting the target method, type, and arguments from a method-call expression tree; other body types (e.g., a property access MemberExpression, a bare constant, an assignment, or a lambda invoking a delegate) do not represent an invokable method and cannot be serialized. This fires synchronously at enqueue time before any storage interaction.
Source
Thrown at src/Hangfire.Core/Common/Job.cs:427
/// </remarks>
public static Job FromExpression<TType>([NotNull, InstantHandle] Expression<Func<TType, Task>> methodCall)
{
return FromExpression(methodCall, null);
}
public static Job FromExpression<TType>([NotNull, InstantHandle] Expression<Func<TType, Task>> methodCall, [CanBeNull] string queue)
{
return FromExpression(methodCall, typeof(TType), queue);
}
private static Job FromExpression([NotNull] LambdaExpression methodCall, [CanBeNull] Type explicitType, [CanBeNull] string queue)
{
if (methodCall == null) throw new ArgumentNullException(nameof(methodCall));
var callExpression = methodCall.Body as MethodCallExpression;
if (callExpression == null)
{
throw new ArgumentException("Expression body should be of type `MethodCallExpression`", nameof(methodCall));
}
var type = explicitType ?? callExpression.Method.DeclaringType;
var method = callExpression.Method;
if (explicitType == null && callExpression.Object != null)
{
// Creating a job that is based on a scope variable. We should infer its
// type and method based on its value, and not from the expression tree.
// TODO: BREAKING: Consider removing this special case entirely.
// People consider that the whole object is serialized, this is not true.
var objectValue = GetExpressionValue(callExpression.Object);
if (objectValue == null)
{
throw new InvalidOperationException("Expression object should be not null.");
}View on GitHub (pinned to c236dd0f93)
Solutions
- Ensure the lambda body is a direct method call, e.g. () => service.DoWork(arg).
- If targeting an instance method, use the typed overload Enqueue<T>(x => x.DoWork()).
- When building expressions dynamically, construct a MethodCallExpression via Expression.Call before wrapping in a lambda.
Example fix
// before — body is not a method call BackgroundJob.Enqueue(() => _counter); BackgroundJob.Enqueue(() => func.Invoke()); // after BackgroundJob.Enqueue(() => service.ReadCounter()); BackgroundJob.Enqueue<MyService>(x => x.DoWork());
Defensive patterns
Strategy: type-guard
Validate before calling
if (methodCall.Body is not MethodCallExpression)
throw new ArgumentException("Lambda body must be a method call, e.g. () => service.Method(args).", nameof(methodCall)); Type guard
public static bool IsMethodCallExpression<T>(Expression<T> expr)
=> expr?.Body is MethodCallExpression; Try / catch
try { var job = Job.FromExpression(() => svc.Work()); }
catch (ArgumentException ex) when (ex.Message.Contains("MethodCallExpression")) { /* rewrite the lambda to a real method call */ } Prevention
- Always write enqueue lambdas as a single method call: () => receiver.Method(args).
- Avoid property reads, field reads, delegate invocations, and compound expressions as the lambda body.
- Prefer the typed overloads BackgroundJob.Enqueue<T>(x => x.Method()) for clarity.
When it happens
Trigger: Passing () => someProperty, () => 42, () => field, or () => func.Invoke() to BackgroundJob.Enqueue/FromExpression; wrapping the method call in an extra lambda; using a ternary or null-coalescing expression as the body; passing an expression that constructs an object rather than calling a method.
Common situations: Refactoring a method call into a property accessor or field read; mistakenly enqueuing a delegate invocation; building expressions dynamically with Expression.Lambda where the body is not a MethodCall; copy-paste errors where the lambda body is a value not a call.
Related errors
- Expression object should be not null.
- The type `{method.DeclaringType}` must be derived from the `
- Argument count must be equal to method parameter count.
- context
- Could not get a value of the job parameter `{name}`. See inn
AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13).
Data as JSON: /api/errors/0c36b86d346a7433.
Report an issue: GitHub.