github/copilot-sdk · error · InvalidOperationException

An in-process FFI runtime library is already loaded from

Error message

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.

What it means

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.

Solutions

  1. Use a single, consistent library path for all FfiRuntimeHost instances in the process.
  2. Dispose all hosts and restart the process if a different runtime binary is genuinely needed.
  3. In tests, run each library-path variant in a separate test process/assembly.
  4. Ensure config or environment resolving the library path is stable across the process lifetime.

Example fix

// before
Create(@"C:\runtimes\debug\copilot_runtime.dll");
Create(@"C:\runtimes\release\copilot_runtime.dll"); // throws
// after
var path = @"C:\runtimes\debug\copilot_runtime.dll";
Create(path);
Create(path); // same path is fine
Defensive patterns

Strategy: validation

Validate before calling

if (FfiRuntimeHost.CurrentResolvedLibraryPath is { } p && p != libraryPath)
    throw new InvalidOperationException($"Process already uses runtime '{p}'; refusing '{libraryPath}'.");

Try / catch

try { host = FfiRuntimeHost.Create(libraryPath); }
catch (InvalidOperationException ex) when (ex.Message.Contains("already loaded")) {
    host = existingHost; // reuse the previously created host
}

Prevention

When it happens

Trigger: Calling Create twice with different library paths (e.g. different build outputs, architectures, or copied binaries of the runtime) within the same process lifetime.

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

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/94f529a2652d6513. Report an issue: GitHub.

Appendix: source

Thrown at dotnet/src/FfiRuntimeHost.cs:271

    private static string? s_resolvedLibraryPath;

    // A normal (non-pinned) handle to this instance, passed to the native side as
    // the callback's user_data so the static outbound callback can route back here.
    private GCHandle _selfHandle;

    /// <summary>
    /// Registers (once) a process-wide <see cref="NativeLibrary.SetDllImportResolver"/>
    /// that maps <see cref="LibraryName"/> to the absolute <c>runtime.node</c> path so the
    /// <see cref="LibraryImportAttribute"/> stubs resolve. The resolved handle is cached by
    /// the runtime after first use, so all in-process hosts share a single loaded library.
    /// </summary>
    private static void PrepareNativeLibrary(string libraryPath)
    {
        lock (ResolverLock)
        {
            if (s_resolvedLibraryPath is not null && s_resolvedLibraryPath != libraryPath)
            {
                throw new InvalidOperationException(
                    $"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.");
            }
            s_resolvedLibraryPath = libraryPath;
            if (!s_resolverRegistered)
            {
                NativeLibrary.SetDllImportResolver(typeof(FfiRuntimeHost).Assembly, Resolve);
                s_resolverRegistered = true;
            }
        }
    }

    private static IntPtr Resolve(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
    {
        if (libraryName == LibraryName && s_resolvedLibraryPath is not null)
        {
            return NativeLibrary.Load(s_resolvedLibraryPath);
        }

View on GitHub (pinned to cd8cf15dc3)