pardeike/Harmony · error · InvalidOperationException

Multiple loaded HarmonySharedState types prevent safe…

Error message

Multiple loaded HarmonySharedState types prevent safe shared patch updates

What it means

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.

Solutions

  1. Remove duplicate Harmony DLLs so exactly one Harmony assembly (and thus one shared-state type) is loaded
  2. Ensure all plugins reference a shared Harmony instance instead of bundling their own copies
  3. Unify assembly load contexts so the Harmony assembly resolves to one loaded copy
  4. Upgrade to a Harmony version that handles multiple shared-state assemblies more gracefully, if available

Example fix

// before (each plugin ships its own)
plugins/ModA/0Harmony.dll
plugins/ModB/0Harmony.dll
// after (single shared copy)
plugins/0Harmony.dll  // ModA and ModB both reference this one
Defensive patterns

Strategy: validation

Validate before calling

var dupes = AppDomain.CurrentDomain.GetAssemblies()
    .Where(a => a.GetName().Name == "0Harmony").Select(a => a.FullName).ToList();
if (dupes.Count > 1) throw new InvalidOperationException("Multiple Harmony assemblies loaded: " + string.Join(", ", dupes));

Try / catch

try { harmony.Patch(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Multiple loaded HarmonySharedState")) { /* resolve duplicate assemblies before continuing */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15). Data as JSON: /api/errors/b08ad562900cf3c3. Report an issue: GitHub.

Appendix: source

Thrown at Harmony/Internal/HarmonySharedState.cs:103

				// copy 'originals' over to our fields
				originals = [];
				if (originalsField != null) // may not exist in older versions
					originals = (Dictionary<MethodInfo, MethodBase>)originalsField.GetValue(null);

				// copy 'originalsMono' over to our fields
				originalsMono = [];
				if (originalsMonoField != null) // may not exist in older versions
					originalsMono = (Dictionary<long, MethodBase[]>)originalsMonoField.GetValue(null);
			}
		}

		// creates a dynamic 'global' type if it does not exist
		static Type GetOrCreateSharedStateType()
		{
			var existing = AppDomain.CurrentDomain.GetAssemblies()
				.Where(assembly => assembly.GetName().Name == name)
				.Select(assembly => assembly.GetType(name, false)).Where(type => type is not null).Distinct().ToArray();
			if (existing.Length > 1) throw new InvalidOperationException("Multiple loaded HarmonySharedState types prevent safe shared patch updates");
			if (existing.Length == 1) return existing[0];

			using var module = ModuleDefinition.CreateModule(name, new ModuleParameters() { Kind = ModuleKind.Dll, ReflectionImporterProvider = MMReflectionImporter.Provider });
			var attr = Mono.Cecil.TypeAttributes.Public | Mono.Cecil.TypeAttributes.Abstract | Mono.Cecil.TypeAttributes.Sealed | Mono.Cecil.TypeAttributes.Class;
			var typedef = new TypeDefinition("", name, attr) { BaseType = module.TypeSystem.Object };
			module.Types.Add(typedef);

			typedef.Fields.Add(new FieldDefinition(
				"state",
				Mono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.Static,
				module.ImportReference(typeof(Dictionary<MethodBase, byte[]>))
			));

			typedef.Fields.Add(new FieldDefinition(
				"originals",
				Mono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.Static,
				module.ImportReference(typeof(Dictionary<MethodInfo, MethodBase>))
			));

View on GitHub (pinned to e7872dc170)