System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
ID: dotnet/target-invocation-exception
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 8 | active | — | — | — |
Root Cause
TargetInvocationException is a wrapper exception thrown by .NET reflection APIs (MethodInfo.Invoke, Activator.CreateInstance, ConstructorInfo.Invoke, PropertyInfo.SetValue, etc.) when the invoked method or constructor throws an exception internally. The actual error is always in the InnerException. This exception is extremely common in DI containers, serialization frameworks, source generators at runtime, MVC model binding, and any framework that uses reflection to instantiate types or invoke methods. On .NET 8 Linux, additional causes include missing native dependencies during reflected constructor calls and trimming-related issues when using AOT compilation. Fix by inspecting InnerException, and where possible, replacing reflection with source generators or compile-time approaches.
genericWorkarounds
-
90% success Unwrap InnerException using ExceptionDispatchInfo to preserve the original stack trace
Use ExceptionDispatchInfo to re-throw the inner exception while preserving its original stack trace: try { methodInfo.Invoke(target, args); } catch (TargetInvocationException ex) when (ex.InnerException != null) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); }. This gives you the actual exception type and the exact line where the failure occurred inside the invoked method. In logging scenarios: logger.LogError(ex.InnerException, "Reflected method {Method} failed", methodInfo.Name); -
88% success Replace reflection-based invocation with compiled delegates or source generators
For frequently invoked reflected methods, compile to a delegate once: var method = typeof(MyService).GetMethod("Process")!; var func = (Func<MyService, string, Task<Result>>)Delegate.CreateDelegate(typeof(Func<MyService, string, Task<Result>>), method); // Now calls through func() throw the original exception, not TargetInvocationException: var result = await func(service, input); For constructor invocation, use compiled expressions: var ctor = typeof(MyService).GetConstructor(new[] { typeof(ILogger) })!; var param = Expression.Parameter(typeof(ILogger)); var lambda = Expression.Lambda<Func<ILogger, MyService>>(Expression.New(ctor, param), param).Compile(); -
86% success Use the .NET 8 MethodInvoker or ConstructorInvoker APIs for better performance and exception handling
In .NET 8, use the new MethodInvoker API that provides better performance and can optionally unwrap exceptions: var invoker = MethodInvoker.Create(methodInfo); try { invoker.Invoke(target, arg1, arg2); } catch (TargetInvocationException ex) { // Still wraps, but the invoker is significantly faster than MethodInfo.Invoke // Use ExceptionDispatchInfo to unwrap as needed ExceptionDispatchInfo.Capture(ex.InnerException!).Throw(); }. For constructors: var ctorInvoker = ConstructorInvoker.Create(constructorInfo); var instance = ctorInvoker.Invoke(arg1);
Dead Ends
Common approaches that don't work:
-
Debugging based on the outer TargetInvocationException message and stack trace
85% fail
The outer exception message is always the same generic string ('Exception has been thrown by the target of an invocation') and the outer stack trace points to the reflection infrastructure (MethodBase.Invoke, RuntimeMethodHandle.InvokeMethod), not to the actual failing code. All diagnostic information is in InnerException. Developers who do not unwrap the InnerException waste time investigating the reflection call site instead of the actual error location
-
Catching TargetInvocationException and re-throwing without preserving the inner exception
80% fail
Using 'throw ex.InnerException;' resets the stack trace, losing the original call site information. Using 'throw;' re-throws the TargetInvocationException wrapper, which is not helpful. Both approaches lose critical debugging information. The correct unwrapping requires ExceptionDispatchInfo to preserve the original stack trace
-
Adding [DebuggerStepThrough] or [DebuggerHidden] attributes to suppress the exception in the debugger
95% fail
These attributes only affect the debugger experience and do not fix the underlying exception. The TargetInvocationException still occurs at runtime. In production, the same error will crash the application or produce incorrect behavior. These attributes just make the error harder to diagnose during development