pardeike/Harmony · error · ApplicationException
The type must declare an empty constructor (the constructor…
Error message
The type {0} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public). What it means
FastAccess.CreateInstantiationHandler builds a compiled delegate that calls T's parameterless constructor via IL. Harmony looks up a zero-argument constructor (any visibility) with reflection; if the type declares none, no instantiation delegate can be emitted and this ApplicationException is thrown.
Solutions
- Add a parameterless constructor to type T (any accessibility: private, internal, protected, protected internal, or public)
- If you cannot modify T, create a dedicated adapter/handler type with an empty constructor
- Point the instantiation handler at a different type that has a default constructor
- Use Activator.CreateInstance with explicit constructor arguments instead of the fast instantiation handler
Example fix
// before
class Config { public Config(string name) { Name = name; } }
var h = FastAccess.CreateInstantiationHandler<Config>();
// after
class Config { public Config() { } public Config(string name) { Name = name; } }
var h = FastAccess.CreateInstantiationHandler<Config>(); Defensive patterns
Strategy: validation
Validate before calling
var hasDefaultCtor = typeof(T).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) is not null;
if (!hasDefaultCtor) throw new InvalidOperationException($"{typeof(T)} needs a parameterless constructor before CreateInstantiationHandler<T>()"); Type guard
static bool HasEmptyConstructor<T>() => typeof(T).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) is not null;
Prevention
- Always give types used with FastAccess/AccessTools instantiation a parameterless constructor
- Check types with a reflection guard before passing them to instantiation helpers
- Watch out for primary constructors / required members which remove the implicit default constructor
- Write a unit test that walks the types you instantiate and asserts each has an empty constructor
When it happens
Trigger: Calling CreateInstantiationHandler<T>() (or APIs that use it, e.g. accessing patch/unpatch shared state via AccessTools-created instances) where typeof(T) has no empty constructor — including structs with fields-only, classes with only parameterized constructors, or anonymous types with required args.
Common situations: Using Harmony's AccessTools/FastAccess helpers on a class after adding a constructor with parameters; types defined with required members or primary constructors without a default; plugin authors instantiating config/option classes that only define (string, int) style constructors.
Related errors
- Value cannot be null. (Parameter 'fromMethod')
- Value cannot be null. (Parameter 'method')
- Can not get IL bytes of method
- Instruction offset is less than 0
- Instruction offset is outside valid range 0
AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15).
Data as JSON: /api/errors/50c03f7587676bc9.
Report an issue: GitHub.
Appendix: source
Thrown at Harmony/Extras/FastAccess.cs:52
/// <returns>An delegate</returns>
///
public delegate T InstantiationHandler<out T>();
/// <summary>A helper class for fast access to getters and setters</summary>
public static class FastAccess
{
/// <summary>Creates an instantiation delegate</summary>
/// <typeparam name="T">Type that constructor creates</typeparam>
/// <returns>The new instantiation delegate</returns>
///
public static InstantiationHandler<T> CreateInstantiationHandler<T>()
{
var constructorInfo =
typeof(T).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null,
[], null);
if (constructorInfo is null)
{
throw new ApplicationException(string.Format(
"The type {0} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public).",
typeof(T)));
}
var dynamicMethod = new DynamicMethodDefinition($"InstantiateObject_{typeof(T).Name}", typeof(T), null);
var generator = dynamicMethod.GetILGenerator();
generator.Emit(OpCodes.Newobj, constructorInfo);
generator.Emit(OpCodes.Ret);
return dynamicMethod.Generate().CreateDelegate<InstantiationHandler<T>>();
}
/// <summary>Creates an getter delegate for a property</summary>
/// <typeparam name="T">Type that getter reads property from</typeparam>
/// <typeparam name="S">Type of the property that gets accessed</typeparam>
/// <param name="propertyInfo">The property</param>
/// <returns>The new getter delegate</returns>
///
[Obsolete("Use AccessTools.MethodDelegate<Func<T, S>>(PropertyInfo.GetGetMethod(true))")]
View on GitHub (pinned to e7872dc170)