stride3d/stride · error · FileNotFoundException

Could not locate native library

Error message

Could not locate native library ${libraryName}

What it means

LocateLibrary throws FileNotFoundException when a native library (name plus the platform extension .dll/.so/.dylib, including 'lib'-prefixed and SONAME-versioned variants on Unix) cannot be found in the packaged native-dependency cache or under the owner assembly's runtimes/{platform}-{cpu}/native folder. It indicates the native dependency required for P/Invoke is absent from the deployment.

Solutions

  1. Confirm the native library for the current RID exists next to the owner assembly (runtimes/{rid}/native/libname.so / name.dll / libname.dylib).
  2. Ensure the NuGet package ships native assets for all target RIDs or install the matching runtime pack.
  3. On Linux/macOS, verify SONAME-versioned files are present (e.g. libassimp.so.5) since the helper globs variants but needs at least one to exist.
  4. Catch FileNotFoundException and either load from a user-configured path via NativeLibrary.Load or produce an install instruction message.
  5. Check the library name spelling/casing and whether the 'lib' prefix convention is met.

Example fix

// before
var path = NativeLibraryHelper.LocateLibrary("assimp", typeof(App)); // FileNotFoundException on slim publish
// after
string path;
try { path = NativeLibraryHelper.LocateLibrary("assimp", typeof(App)); }
catch (FileNotFoundException ex)
{
    throw new InvalidOperationException(
        "Native library 'assimp' missing. Ensure the Stride native assets package for " +
        $"{RuntimeInformation.RuntimeIdentifier} is referenced.", ex);
}
Defensive patterns

Strategy: try-catch

Validate before calling

static bool NativeLibPresent(string lib, Type owner)
{
    var ext = OperatingSystem.IsWindows() ? ".dll" : OperatingSystem.IsMacOS() ? ".dylib" : ".so";
    var rid = RuntimeInformation.RuntimeIdentifier;
    var dir = Path.Combine(AppContext.BaseDirectory, "runtimes", rid, "native");
    return File.Exists(Path.Combine(dir, lib + ext)) || File.Exists(Path.Combine(dir, "lib" + lib + ext));
}

Try / catch

try
{
    libPath = NativeLibraryHelper.LocateLibrary(libraryName, ownerType);
}
catch (FileNotFoundException ex)
{
    throw new InvalidOperationException(
    $"Native library '{libraryName}' not found for {RuntimeInformation.RuntimeIdentifier}. Install the matching native assets package.", ex);
}

Prevention

When it happens

Trigger: Calling NativeLibraryHelper.LocateLibrary(libraryName, ownerType) where neither 'libraryName.ext' nor, on non-Windows, 'liblibraryName.ext' (nor SONAME-versioned variants) exists next to the owner assembly or in its runtimes-specific native folder.

Common situations: Linux deployment where the .so was not packaged for linux-x64; library renamed or version-bumped (libfoo.so.6 vs libfoo.so.7 mismatch); self-contained publish that trimmed native assets; running tests from a directory lacking the runtimes layout; package depends on a system library the user hasn't installed.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/0c0a300d568b3b73. Report an issue: GitHub.

Appendix: source

Thrown at sources/core/Stride.Core/Native/NativeLibraryHelper.cs:156

        {
            var nameWithExtension = name + libExtension;

            // Resolver-registered natives: the extension-less map is keyed by SONAME-stripped base name
            // (matches libassimp.so.6); the with-extension map only matches an exact unversioned filename.
            if (nativeDependenciesWithoutExtensions.TryGetValue(name, out string? knownPath)
                || nativeDependenciesWithExtensions.TryGetValue(nameWithExtension, out knownPath))
                return knownPath;

            // Try in current path
            if (File.Exists(nameWithExtension))
                return nameWithExtension;

            // Try runtimes specific path (globs SONAME-versioned variants, picks highest)
            if (TryFindLibraryPath(ownerType, nameWithExtension, out knownPath))
                return knownPath;
        }

        throw new FileNotFoundException($"Could not locate native library {libraryName}");
    }

    /// <summary>
    ///   Preloads a native library (or returns the handle of one already loaded), so subsequent P/Invoke
    ///   calls use it instead of triggering their own load. The returned OS module handle also lets callers
    ///   that resolve exports themselves (for example, libraries with their own function-pointer binding)
    ///   share the engine's native resolution.
    /// </summary>
    /// <param name="libraryName">The name of the library, without the extension.</param>
    /// <param name="ownerType">
    ///   The <see cref="Type"/> whose Assembly location is related to the native library.
    ///   This is needed because <see cref="Assembly.GetCallingAssembly"/> cannot be used,
    ///   as it might be wrong due to optimizations.
    /// </param>
    /// <returns>The OS module handle of the loaded library.</returns>
    /// <exception cref="DllNotFoundException">
    ///   The library with name <paramref name="libraryName"/> could not be loaded.
    /// </exception>

View on GitHub (pinned to 96fad776d2)