{"record":{"id":"94f529a2652d6513","repo":"github/copilot-sdk","slug":"an-in-process-ffi-runtime-library-is-already-loade-94f529","errorCode":null,"errorMessage":"An in-process FFI runtime library is already loaded from '{s_resolvedLibraryPath}'; loading a different library from '{libraryPath}' in the same process is not supported.","messagePattern":"An in-process FFI runtime library is already loaded from '(.+?)'; loading a different library from '(.+?)' in the same process is not supported\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/FfiRuntimeHost.cs","lineNumber":271,"sourceCode":"    private static string? s_resolvedLibraryPath;\n\n    // A normal (non-pinned) handle to this instance, passed to the native side as\n    // the callback's user_data so the static outbound callback can route back here.\n    private GCHandle _selfHandle;\n\n    /// <summary>\n    /// Registers (once) a process-wide <see cref=\"NativeLibrary.SetDllImportResolver\"/>\n    /// that maps <see cref=\"LibraryName\"/> to the absolute <c>runtime.node</c> path so the\n    /// <see cref=\"LibraryImportAttribute\"/> stubs resolve. The resolved handle is cached by\n    /// the runtime after first use, so all in-process hosts share a single loaded library.\n    /// </summary>\n    private static void PrepareNativeLibrary(string libraryPath)\n    {\n        lock (ResolverLock)\n        {\n            if (s_resolvedLibraryPath is not null && s_resolvedLibraryPath != libraryPath)\n            {\n                throw new InvalidOperationException(\n                    $\"An in-process FFI runtime library is already loaded from '{s_resolvedLibraryPath}'; \"\n                    + $\"loading a different library from '{libraryPath}' in the same process is not supported.\");\n            }\n            s_resolvedLibraryPath = libraryPath;\n            if (!s_resolverRegistered)\n            {\n                NativeLibrary.SetDllImportResolver(typeof(FfiRuntimeHost).Assembly, Resolve);\n                s_resolverRegistered = true;\n            }\n        }\n    }\n\n    private static IntPtr Resolve(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)\n    {\n        if (libraryName == LibraryName && s_resolvedLibraryPath is not null)\n        {\n            return NativeLibrary.Load(s_resolvedLibraryPath);\n        }","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/dotnet/src/FfiRuntimeHost.cs#L253-L289","documentation":"PrepareNativeLibrary enforces that only one FFI runtime library path can ever be resolved per process (guarded by ResolverLock). If a first Create registered s_resolvedLibraryPath and a later call passes a different libraryPath, the check throws InvalidOperationException — two distinct in-process runtimes in one process are unsupported.","triggerScenarios":"Calling Create twice with different library paths (e.g. different build outputs, architectures, or copied binaries of the runtime) within the same process lifetime.","commonSituations":"Tests that point at Debug and Release builds of the native lib in one xUnit process; app reconfiguration changing the runtime path after the first host was created; hot-swapping a rebuilt native DLL.","solutions":["Use a single, consistent library path for all FfiRuntimeHost instances in the process.","Dispose all hosts and restart the process if a different runtime binary is genuinely needed.","In tests, run each library-path variant in a separate test process/assembly.","Ensure config or environment resolving the library path is stable across the process lifetime."],"exampleFix":"// before\nCreate(@\"C:\\runtimes\\debug\\copilot_runtime.dll\");\nCreate(@\"C:\\runtimes\\release\\copilot_runtime.dll\"); // throws\n// after\nvar path = @\"C:\\runtimes\\debug\\copilot_runtime.dll\";\nCreate(path);\nCreate(path); // same path is fine","handlingStrategy":"validation","validationCode":"if (FfiRuntimeHost.CurrentResolvedLibraryPath is { } p && p != libraryPath)\n    throw new InvalidOperationException($\"Process already uses runtime '{p}'; refusing '{libraryPath}'.\");","typeGuard":null,"tryCatchPattern":"try { host = FfiRuntimeHost.Create(libraryPath); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"already loaded\")) {\n    host = existingHost; // reuse the previously created host\n}","preventionTips":["Resolve the library path once per process and cache it","Never vary the runtime path via mutable config at runtime","Isolate library-path variants in separate test processes","Reuse a single FfiRuntimeHost instance"],"tags":["ffi","native-library","process-lifetime","duplicate-load"],"backgroundTag":"module-init-failed","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}