{"record":{"id":"b08ad562900cf3c3","repo":"pardeike/Harmony","slug":"multiple-loaded-harmonysharedstate-types-prevent-safe-shared","errorCode":null,"errorMessage":"Multiple loaded HarmonySharedState types prevent safe shared patch updates","messagePattern":"Multiple loaded HarmonySharedState types prevent safe shared patch updates","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"Harmony/Internal/HarmonySharedState.cs","lineNumber":103,"sourceCode":"\t\t\t\t// copy 'originals' over to our fields\r\n\t\t\t\toriginals = [];\r\n\t\t\t\tif (originalsField != null) // may not exist in older versions\r\n\t\t\t\t\toriginals = (Dictionary<MethodInfo, MethodBase>)originalsField.GetValue(null);\r\n\r\n\t\t\t\t// copy 'originalsMono' over to our fields\r\n\t\t\t\toriginalsMono = [];\r\n\t\t\t\tif (originalsMonoField != null) // may not exist in older versions\r\n\t\t\t\t\toriginalsMono = (Dictionary<long, MethodBase[]>)originalsMonoField.GetValue(null);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// creates a dynamic 'global' type if it does not exist\r\n\t\tstatic Type GetOrCreateSharedStateType()\r\n\t\t{\r\n\t\t\tvar existing = AppDomain.CurrentDomain.GetAssemblies()\r\n\t\t\t\t.Where(assembly => assembly.GetName().Name == name)\r\n\t\t\t\t.Select(assembly => assembly.GetType(name, false)).Where(type => type is not null).Distinct().ToArray();\r\n\t\t\tif (existing.Length > 1) throw new InvalidOperationException(\"Multiple loaded HarmonySharedState types prevent safe shared patch updates\");\r\n\t\t\tif (existing.Length == 1) return existing[0];\r\n\r\n\t\t\tusing var module = ModuleDefinition.CreateModule(name, new ModuleParameters() { Kind = ModuleKind.Dll, ReflectionImporterProvider = MMReflectionImporter.Provider });\r\n\t\t\tvar attr = Mono.Cecil.TypeAttributes.Public | Mono.Cecil.TypeAttributes.Abstract | Mono.Cecil.TypeAttributes.Sealed | Mono.Cecil.TypeAttributes.Class;\r\n\t\t\tvar typedef = new TypeDefinition(\"\", name, attr) { BaseType = module.TypeSystem.Object };\r\n\t\t\tmodule.Types.Add(typedef);\r\n\r\n\t\t\ttypedef.Fields.Add(new FieldDefinition(\r\n\t\t\t\t\"state\",\r\n\t\t\t\tMono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.Static,\r\n\t\t\t\tmodule.ImportReference(typeof(Dictionary<MethodBase, byte[]>))\r\n\t\t\t));\r\n\r\n\t\t\ttypedef.Fields.Add(new FieldDefinition(\r\n\t\t\t\t\"originals\",\r\n\t\t\t\tMono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.Static,\r\n\t\t\t\tmodule.ImportReference(typeof(Dictionary<MethodInfo, MethodBase>))\r\n\t\t\t));\r","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Internal/HarmonySharedState.cs#L85-L121","documentation":"HarmonySharedState stores patch state in a dynamically generated assembly-level type named after the shared-state module. GetOrCreateSharedStateType scans all loaded assemblies for a type with that name; if more than one such assembly/type is loaded (e.g. the same assembly loaded twice or two Harmony copies in different load contexts), safe shared patch updates are impossible and it throws InvalidOperationException.","triggerScenarios":"GetOrCreateSharedStateType finding two or more distinct types named HarmonySharedState across AppDomain.CurrentDomain.GetAssemblies() — typically the Harmony assembly loaded twice (different paths/versions), plugin load contexts (AssemblyLoadContext) duplicating assemblies, or byte-loading Harmony from multiple sources.","commonSituations":"Unity/BepInEx mods each bundling their own Harmony 2.x DLL; hot-reload creating duplicate assemblies; mixing NuGet Harmony with a bundled copy in the same app domain.","solutions":["Remove duplicate Harmony DLLs so exactly one Harmony assembly (and thus one shared-state type) is loaded","Ensure all plugins reference a shared Harmony instance instead of bundling their own copies","Unify assembly load contexts so the Harmony assembly resolves to one loaded copy","Upgrade to a Harmony version that handles multiple shared-state assemblies more gracefully, if available"],"exampleFix":"// before (each plugin ships its own)\nplugins/ModA/0Harmony.dll\nplugins/ModB/0Harmony.dll\n// after (single shared copy)\nplugins/0Harmony.dll  // ModA and ModB both reference this one","handlingStrategy":"validation","validationCode":"var dupes = AppDomain.CurrentDomain.GetAssemblies()\n    .Where(a => a.GetName().Name == \"0Harmony\").Select(a => a.FullName).ToList();\nif (dupes.Count > 1) throw new InvalidOperationException(\"Multiple Harmony assemblies loaded: \" + string.Join(\", \", dupes));","typeGuard":null,"tryCatchPattern":"try { harmony.Patch(...); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Multiple loaded HarmonySharedState\")) { /* resolve duplicate assemblies before continuing */ }","preventionTips":["Ship exactly one Harmony DLL per application domain; share it across plugins","Avoid loading the same assembly twice via different load contexts or byte-loading","For mod loaders, use a shared/common Harmony package dependency","Log loaded Harmony assemblies at startup to catch duplicates early"],"tags":["assembly-loading","shared-state","duplicate-assemblies","harmony"],"backgroundTag":"duplicate-assembly-loaded","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"}