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

  1. Ensure the lambda body is an actual method call, e.g. GetMethodInfo(() => myInstance.Method(default(ArgType))) using default values for parameters
  2. Use () => SomeType.StaticMethod(default(A)) for static methods; use typeof(SomeType).GetMethod(...) as a fallback when the member is not a method
  3. If targeting a constructor or property, use the matching helper (GetConstructorInfo, GetPropertyInfo, GetFieldInfo) instead
  4. 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

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


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)