{"record":{"id":"78a1f82222ef44c8","repo":"pardeike/Harmony","slug":"invalid-expression-expression-should-consist-of-a-method","errorCode":null,"errorMessage":"Invalid Expression. Expression should consist of a Method call only.","messagePattern":"Invalid Expression\\. Expression should consist of a Method call only\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"Harmony/Tools/SymbolExtensions.cs","lineNumber":44,"sourceCode":"\t\t/// <typeparam name=\"TResult\">The generic result type</typeparam>\r\n\t\t/// <param name=\"expression\">The lambda expression using the method</param>\r\n\t\t/// <returns>The method in the lambda expression</returns>\r\n\t\t///\r\n\t\tpublic static MethodInfo GetMethodInfo<T, TResult>(Expression<Func<T, TResult>> expression) => GetMethodInfo((LambdaExpression)expression);\r\n\r\n\t\t/// <summary>Given a lambda expression that calls a method, returns the method info</summary>\r\n\t\t/// <param name=\"expression\">The lambda expression using the method</param>\r\n\t\t/// <returns>The method in the lambda expression</returns>\r\n\t\t///\r\n\t\tpublic static MethodInfo GetMethodInfo(LambdaExpression expression)\r\n\t\t{\r\n\t\t\tvar outermostExpression = expression.Body as MethodCallExpression;\r\n\r\n\t\t\tif (outermostExpression is null)\r\n\t\t\t{\r\n\t\t\t\tif (expression.Body is UnaryExpression ue && ue.Operand is MethodCallExpression me && me.Object is System.Linq.Expressions.ConstantExpression ce && ce.Value is MethodInfo mi)\r\n\t\t\t\t\treturn mi;\r\n\t\t\t\tthrow new ArgumentException(\"Invalid Expression. Expression should consist of a Method call only.\");\r\n\t\t\t}\r\n\r\n\t\t\tvar method = outermostExpression.Method;\r\n\t\t\tif (method is null)\r\n\t\t\t\tthrow new Exception($\"Cannot find method for expression {expression}\");\r\n\r\n\t\t\treturn method;\r\n\t\t}\r\n\r\n\t\t/// <summary>Given a lambda expression that accesses a field, returns the field info</summary>\r\n\t\t/// <typeparam name=\"T\">The generic field type</typeparam>\r\n\t\t/// <param name=\"expression\">The lambda expression using the field</param>\r\n\t\t/// <returns>The field in the lambda expression</returns>\r\n\t\t///\r\n\t\tpublic static FieldInfo GetFieldInfo<T>(Expression<Func<T>> expression) => GetFieldInfo((LambdaExpression)expression);\r\n\r\n\t\t/// <summary>Given a lambda expression that accesses a field, returns the field info</summary>\r\n\t\t/// <param name=\"expression\">The lambda expression using the field</param>\r","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Tools/SymbolExtensions.cs#L26-L62","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nvar m = HarmonyLib.SymbolExtensions.GetMethodInfo(() => new StringBuilder());\n// after\nvar m = HarmonyLib.SymbolExtensions.GetMethodInfo(() => new StringBuilder().ToString());","handlingStrategy":"validation","validationCode":"var expr = (Expression<Func<Ret>>) (() => target.Method(default(Arg)));\nif (expr.Body is not MethodCallExpression &&\n    expr.Body is not UnaryExpression { Operand: MethodCallExpression })\n    throw new InvalidOperationException(\"Lambda must be a direct method call\");","typeGuard":"static MethodInfo TryGetMethodInfo(LambdaExpression e) =>\n    e.Body switch\n    {\n        MethodCallExpression mce => mce.Method,\n        UnaryExpression { Operand: MethodCallExpression m } => m.Method,\n        _ => null\n    };","tryCatchPattern":"MethodInfo mi;\ntry { mi = SymbolExtensions.GetMethodInfo(() => target.Method(default(A))); }\ncatch (ArgumentException ex) { mi = typeof(Target).GetMethod(\"Method\", new[] { typeof(A) }); }","preventionTips":["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"],"tags":["argument-exception","lambda-expression","reflection"],"backgroundTag":"invalid-argument-value","analyzedSha":"e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c","analyzedAt":"2026-09-15T22:47:11.550Z","contentChangedAt":"2026-09-15T22:47:11.550Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}