pardeike/Harmony · error · ArgumentException

Invalid Expression. Expression should consist of a field…

Error message

Invalid Expression. Expression should consist of a field access only.

What it means

AccessTools.GetFieldInfo extracts a FieldInfo from a lambda, but only when the lambda body (after stripping Convert/ConvertChecked wrappers) is a member expression referencing a field. If the body is a property, method, or something else entirely, ArgumentException is thrown because the expression must consist of a field access only.

Solutions

  1. Confirm the lambda accesses an actual C# field, e.g. GetFieldInfo(() => instance.someField) or a static field GetFieldInfo(() => SomeType.staticField)
  2. If the member is a property, use AccessTools.Property / GetPropertyInfo or the corresponding SymbolExtensions helper instead
  3. If the member changed from field to property upstream, switch the helper or pin to the older type version
  4. As a fallback use typeof(T).GetField("name", BindingFlags) directly

Example fix

// before
var f = SymbolExtensions.GetFieldInfo(() => instance.SomeAutoProperty); // property => throws
// after
var f = typeof(Instance).GetField("someField", BindingFlags.NonPublic | BindingFlags.Instance);
Defensive patterns

Strategy: validation

Validate before calling

Expression body = expr.Body;
while (body is UnaryExpression { NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked } u) body = u.Operand;
bool isField = body is MemberExpression { Member: FieldInfo };

Type guard

static FieldInfo TryGetFieldInfo(LambdaExpression e)
{
    Expression b = e.Body;
    while (b is UnaryExpression u) b = u.Operand;
    return b is MemberExpression me && me.Member is FieldInfo fi ? fi : null;
}

Try / catch

FieldInfo fi;
try { fi = SymbolExtensions.GetFieldInfo(() => instance.someField); }
catch (ArgumentException) { fi = typeof(T).GetField("someField", BindingFlags.NonPublic | BindingFlags.Instance); }

Prevention

When it happens

Trigger: Calling SymbolExtensions.GetFieldInfo(() => obj.SomeProperty) (property, not field), (() => obj.Method()) (call), or a lambda whose body is a call or index expression; also when the member expression does not resolve to a FieldInfo after unary conversions are unwrapped.

Common situations: The target member was refactored from a public field to a property (very common when a library updates); developer picked the wrong helper (GetFieldInfo vs GetPropertyInfo); boxing conversion over a non-field member.

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/72b5ad487ae73e21. Report an issue: GitHub.

Appendix: source

Thrown at Harmony/Tools/SymbolExtensions.cs:76

		///
		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>
		/// <returns>The field in the lambda expression</returns>
		///
		public static FieldInfo GetFieldInfo(LambdaExpression expression)
		{
			if (expression is null)
				throw new ArgumentNullException(nameof(expression));

			var body = expression.Body;
			while (body is UnaryExpression { NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked } unaryExpression)
				body = unaryExpression.Operand;

			if (body is MemberExpression memberExpression && memberExpression.Member is FieldInfo field)
				return field;
			throw new ArgumentException("Invalid Expression. Expression should consist of a field access only.", nameof(expression));
		}
	}
}

View on GitHub (pinned to e7872dc170)