{"record":{"id":"179f3ec5d3e15d19","repo":"pardeike/Harmony","slug":"unexpected-end-of-stack-trace","errorCode":null,"errorMessage":"Unexpected end of stack trace","messagePattern":"Unexpected end of stack trace","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"Harmony/Tools/AccessTools.cs","lineNumber":2019,"sourceCode":"\t\t\tvar method = harmonyMethod.GetOriginalMethod() as MethodInfo;\r\n\t\t\tif (method is null)\r\n\t\t\t\tthrow new NullReferenceException($\"Delegate {typeof(DelegateType)} has no defined original method\");\r\n\t\t\treturn MethodDelegate<DelegateType>(method, instance, harmonyMethod.nonVirtualDelegate is false, null);\r\n\t\t}\r\n\r\n\t\t/// <summary>Returns who called the current method</summary>\r\n\t\t/// <returns>The calling method/constructor (excluding the caller)</returns>\r\n\t\t///\r\n\t\tpublic static MethodBase GetOutsideCaller()\r\n\t\t{\r\n\t\t\tvar trace = new StackTrace(true);\r\n\t\t\tforeach (var frame in trace.GetFrames())\r\n\t\t\t{\r\n\t\t\t\tvar method = frame.GetMethod();\r\n\t\t\t\tif (method.DeclaringType?.Namespace != typeof(Harmony).Namespace)\r\n\t\t\t\t\treturn method;\r\n\t\t\t}\r\n\t\t\tthrow new Exception(\"Unexpected end of stack trace\");\r\n\t\t}\r\n\r\n#if NET35\r\n\t\tstatic readonly MethodInfo m_PrepForRemoting = Method(typeof(Exception), \"PrepForRemoting\") // MS .NET\r\n\t\t\t?? Method(typeof(Exception), \"FixRemotingException\"); // mono .NET\r\n\t\tstatic readonly FastInvokeHandler PrepForRemoting = MethodInvoker.GetHandler(m_PrepForRemoting);\r\n#endif\r\n\r\n\t\t/// <summary>Rethrows an exception while preserving its stack trace (throw statement typically clobbers existing stack traces)</summary>\r\n\t\t/// <param name=\"exception\">The exception to rethrow</param>\r\n\t\t///\r\n\t\tpublic static void RethrowException(Exception exception)\r\n\t\t{\r\n#if NET35\r\n\t\t\t_ = PrepForRemoting(exception);\r\n#else\r\n\t\t\tSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(exception).Throw();\r\n#endif\r","sourceCodeStart":2001,"sourceCodeEnd":2037,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Tools/AccessTools.cs#L2001-L2037","documentation":"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.","triggerScenarios":"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.","commonSituations":"Using caller-detection APIs inside Harmony patches compiled into Harmony-named dynamic methods, or exotic runtimes that truncate stack traces.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"var frames = new StackTrace().GetFrames() ?? Array.Empty<StackFrame>();\nvar hasOutside = frames.Any(f => f.GetMethod()?.DeclaringType?.Namespace != \"Harmony\");","typeGuard":null,"tryCatchPattern":"try { var caller = AccessTools.GetOutsideCaller(); }\ncatch (Exception ex) when (ex.Message == \"Unexpected end of stack trace\") { /* called from inside Harmony-compiled code; pass explicit MethodInfo instead */ }","preventionTips":["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."],"tags":["stack-trace","reflection","harmony"],"backgroundTag":"internal-invariant-violation","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"}