pardeike/Harmony · error · ArgumentException
Invalid Expression. Expression should consist of a Method…
Error message
Invalid Expression. Expression should consist of a Method call only.
What it means
AccessTools.GetMethodInfo extracts a MethodInfo from a lambda expression, but only when the lambda body is a method-call expression (or, as a special case, a conversion of a constant MethodInfo). If the lambda body is anything else — a constructor call, property access, delegate creation, or plain expression — ArgumentException is thrown telling the developer the expression must consist of a method call only.
Solutions
- Ensure the lambda body is an actual method call, e.g. GetMethodInfo(() => myInstance.Method(default(ArgType))) using default values for parameters
- Use () => SomeType.StaticMethod(default(A)) for static methods; use typeof(SomeType).GetMethod(...) as a fallback when the member is not a method
- If targeting a constructor or property, use the matching helper (GetConstructorInfo, GetPropertyInfo, GetFieldInfo) instead
- For lambdas returning MethodInfo constants, verify the conversion path is preserved; otherwise call the method-returning member directly
Example fix
// before var m = HarmonyLib.SymbolExtensions.GetMethodInfo(() => new StringBuilder()); // after var m = HarmonyLib.SymbolExtensions.GetMethodInfo(() => new StringBuilder().ToString());
Defensive patterns
Strategy: validation
Validate before calling
var expr = (Expression<Func<Ret>>) (() => target.Method(default(Arg)));
if (expr.Body is not MethodCallExpression &&
expr.Body is not UnaryExpression { Operand: MethodCallExpression })
throw new InvalidOperationException("Lambda must be a direct method call"); Type guard
static MethodInfo TryGetMethodInfo(LambdaExpression e) =>
e.Body switch
{
MethodCallExpression mce => mce.Method,
UnaryExpression { Operand: MethodCallExpression m } => m.Method,
_ => null
}; Try / catch
MethodInfo mi;
try { mi = SymbolExtensions.GetMethodInfo(() => target.Method(default(A))); }
catch (ArgumentException ex) { mi = typeof(Target).GetMethod("Method", new[] { typeof(A) }); } Prevention
- Lambdas for GetMethodInfo must call a method — supply default(...) for every parameter
- Use the dedicated helper for constructors and properties instead of forcing them into a method-call lambda
- If a member is not a method, fall back to typeof(T).GetMethod with explicit BindingFlags
When it happens
Trigger: Calling SymbolExtensions.GetMethodInfo(() => SomeClass.SomeMember) where the lambda body compiles to: a NewExpression (constructor), a MemberExpression (property/field), an InvocationExpression, a binary/unary expression, or a conversion that is not of a constant MethodInfo.
Common situations: Passing () => new Foo() instead of () => new Foo().ToString() or using GetMethodInfo where GetConstructorInfo/GetPropertyInfo was intended; pointing the lambda at a property getter indirectly; refactoring changed the lambda body shape after an overload change.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid Expression. Expression should consist of a field…
- must be specified as 'Namespace.Type1.Type2:MemberName
- Field must be static
- The type must declare an empty constructor (the constructor…
- Value cannot be null. (Parameter 'fromMethod')
AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15).
Data as JSON: /api/errors/78a1f82222ef44c8.
Report an issue: GitHub.
Appendix: source
Thrown at Harmony/Tools/SymbolExtensions.cs:44
/// <typeparam name="TResult">The generic result type</typeparam>
/// <param name="expression">The lambda expression using the method</param>
/// <returns>The method in the lambda expression</returns>
///
public static MethodInfo GetMethodInfo<T, TResult>(Expression<Func<T, TResult>> expression) => GetMethodInfo((LambdaExpression)expression);
/// <summary>Given a lambda expression that calls a method, returns the method info</summary>
/// <param name="expression">The lambda expression using the method</param>
/// <returns>The method in the lambda expression</returns>
///
public static MethodInfo GetMethodInfo(LambdaExpression expression)
{
var outermostExpression = expression.Body as MethodCallExpression;
if (outermostExpression is null)
{
if (expression.Body is UnaryExpression ue && ue.Operand is MethodCallExpression me && me.Object is System.Linq.Expressions.ConstantExpression ce && ce.Value is MethodInfo mi)
return mi;
throw new ArgumentException("Invalid Expression. Expression should consist of a Method call only.");
}
var method = outermostExpression.Method;
if (method is null)
throw new Exception($"Cannot find method for expression {expression}");
return method;
}
/// <summary>Given a lambda expression that accesses a field, returns the field info</summary>
/// <typeparam name="T">The generic field type</typeparam>
/// <param name="expression">The lambda expression using the field</param>
/// <returns>The field in the lambda expression</returns>
///
public static FieldInfo GetFieldInfo<T>(Expression<Func<T>> expression) => GetFieldInfo((LambdaExpression)expression);
/// <summary>Given a lambda expression that accesses a field, returns the field info</summary>
/// <param name="expression">The lambda expression using the field</param>
View on GitHub (pinned to e7872dc170)