pardeike/Harmony · error · Exception
Unexpected end of stack trace
Error message
Unexpected end of stack trace
What it means
AccessTools.GetOutsideCaller walks the current stack trace and returns the first frame outside Harmony's own namespace. It throws a plain Exception('Unexpected end of stack trace') only when every frame belongs to Harmony, i.e. no external caller exists - which should be practically impossible in normal use.
Solutions
- Avoid calling caller-detection AccessTools APIs from inside Harmony-generated code; pass an explicit MethodInfo instead.
- Check the runtime's stack trace behavior (tracing restrictions, trimmed stacks).
- Upgrade Harmony; report the runtime scenario if it persists.
Defensive patterns
Strategy: try-catch
Validate before calling
var frames = new StackTrace().GetFrames() ?? Array.Empty<StackFrame>(); var hasOutside = frames.Any(f => f.GetMethod()?.DeclaringType?.Namespace != "Harmony");
Try / catch
try { var caller = AccessTools.GetOutsideCaller(); }
catch (Exception ex) when (ex.Message == "Unexpected end of stack trace") { /* called from inside Harmony-compiled code; pass explicit MethodInfo instead */ } Prevention
- Do not rely on caller detection from inside patches or Harmony-generated dynamic methods.
- Prefer APIs that accept an explicit MethodInfo over caller-inferring ones.
- Test on your target runtime; some runtimes truncate or rewrite stack traces.
When it happens
Trigger: Calling an AccessTools API that resolves the outside caller from a context where the entire stack consists of Harmony methods - e.g. invoked from within Harmony-compiled dynamic methods/reverse patches that erased or replaced frames, or a filtered/restricted stack trace.
Common situations: Using caller-detection APIs inside Harmony patches compiled into Harmony-named dynamic methods, or exotic runtimes that truncate stack traces.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- The type must declare an empty constructor (the constructor…
- Value cannot be null. (Parameter 'fromMethod')
- Value cannot be null. (Parameter 'method')
- Value cannot be null. (Parameter 'config.original')
- Undefined target method for patch method
AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15).
Data as JSON: /api/errors/179f3ec5d3e15d19.
Report an issue: GitHub.
Appendix: source
Thrown at Harmony/Tools/AccessTools.cs:2019
var method = harmonyMethod.GetOriginalMethod() as MethodInfo;
if (method is null)
throw new NullReferenceException($"Delegate {typeof(DelegateType)} has no defined original method");
return MethodDelegate<DelegateType>(method, instance, harmonyMethod.nonVirtualDelegate is false, null);
}
/// <summary>Returns who called the current method</summary>
/// <returns>The calling method/constructor (excluding the caller)</returns>
///
public static MethodBase GetOutsideCaller()
{
var trace = new StackTrace(true);
foreach (var frame in trace.GetFrames())
{
var method = frame.GetMethod();
if (method.DeclaringType?.Namespace != typeof(Harmony).Namespace)
return method;
}
throw new Exception("Unexpected end of stack trace");
}
#if NET35
static readonly MethodInfo m_PrepForRemoting = Method(typeof(Exception), "PrepForRemoting") // MS .NET
?? Method(typeof(Exception), "FixRemotingException"); // mono .NET
static readonly FastInvokeHandler PrepForRemoting = MethodInvoker.GetHandler(m_PrepForRemoting);
#endif
/// <summary>Rethrows an exception while preserving its stack trace (throw statement typically clobbers existing stack traces)</summary>
/// <param name="exception">The exception to rethrow</param>
///
public static void RethrowException(Exception exception)
{
#if NET35
_ = PrepForRemoting(exception);
#else
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(exception).Throw();
#endif
View on GitHub (pinned to e7872dc170)