AvaloniaUI/Avalonia · error · DllNotFoundException

Unable to load {libraryPath} via dlopen

Error message

Unable to load {libraryPath} via dlopen

What it means

After resolving libHarfBuzzSharp's directory via dlinfo(RTLD_DI_ORIGIN), the workaround dlopen()s the full path with RTLD_NOW|RTLD_DEEPBIND. A zero return means the loader could not open the file. The message string still literally contains '{libraryPath}' (not interpolated), so it is a second bug — the path is never printed.

Source

Thrown at samples/XEmbedSample/HarfbuzzWorkaround.cs:56

    private const int RTLD_NOW = 2;
    private const int RTLD_DEEPBIND = 8;
    
    public static void Apply()
    {
        if (RuntimeInformation.RuntimeIdentifier.Contains("musl"))
            throw new PlatformNotSupportedException("musl doesn't support RTLD_DEEPBIND");
        
        var libraryPathBytes = Marshal.AllocHGlobal(4096);
        var handle = NativeLibrary.Load("libHarfBuzzSharp", typeof(HarfBuzzSharp.Blob).Assembly, null);
        dlinfo(handle, RTLD_DI_ORIGIN, libraryPathBytes);
        var libraryOrigin = Marshal.PtrToStringUTF8(libraryPathBytes) ?? string.Empty;
        Marshal.FreeHGlobal(libraryPathBytes);
        var libraryPath = Path.Combine(libraryOrigin, "libHarfBuzzSharp.so");
        
        NativeLibrary.Free(handle);
        var forceLoadedHandle = dlopen(libraryPath, RTLD_NOW | RTLD_DEEPBIND);
        if (forceLoadedHandle == IntPtr.Zero)
            throw new DllNotFoundException($"Unable to load {libraryPath} via dlopen");
        
        NativeLibrary.SetDllImportResolver(typeof(HarfBuzzSharp.Blob).Assembly, (name, assembly, searchPath) =>
        {
            if (name.Contains("HarfBuzzSharp"))
                return dlopen(libraryPath, RTLD_NOW | RTLD_DEEPBIND);
            return NativeLibrary.Load(name, assembly, searchPath);
        });
        
    }
}

View on GitHub (pinned to 11c5427268)

Solutions

  1. Verify libHarfBuzzSharp.so exists at the resolved libraryOrigin and is readable (note the message bug — print dlerror() instead).
  2. Ensure the published RID matches the runtime (linux-x64 vs linux-arm64 vs linux-musl-x64).
  3. Install missing system dependencies (fontconfig, libstdc++).
  4. Call dlerror() after a zero return and include its message for the real cause.

Example fix

// before
var forceLoadedHandle = dlopen(libraryPath, RTLD_NOW | RTLD_DEEPBIND);
if (forceLoadedHandle == IntPtr.Zero)
    throw new DllNotFoundException($"Unable to load {libraryPath} via dlopen");

// after (interpolate the path AND surface dlerror)
var forceLoadedHandle = dlopen(libraryPath, RTLD_NOW | RTLD_DEEPBIND);
if (forceLoadedHandle == IntPtr.Zero)
{
    var err = Marshal.PtrToStringAnsi(dlerror()) ?? "unknown";
    throw new DllNotFoundException($"Unable to load '{libraryPath}' via dlopen: {err}");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm the native lib is present and loadable
if (!File.Exists(libraryPath)) return; // missing binary for this RID

Type guard

static bool LibExists(string path) => File.Exists(path) && NativeLibrary.TryLoad(path, out _);

Try / catch

try { HarfbuzzWorkaround.Apply(); }
catch (DllNotFoundException ex)
{ _logger.LogError("HarfBuzz native load failed; ensure RID-matched lib exists. {Msg}", ex.Message); }

Prevention

When it happens

Trigger: dlopen returning IntPtr.Zero: the resolved library path does not exist, the file is the wrong architecture (e.g. x64 vs arm64), library is not readable (permissions), or a dependency is missing (libstdc++, libfontconfig). Happens when the RID-packaged native lib is absent from the expected directory.

Common situations: Publishing self-contained with the wrong RID; trimming removing the native HarfBuzzSharp binary; mismatched libHarfBuzzSharp.so ABI; missing system fonts/fontconfig on the host.

Related errors


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/4fec23fe2a8ef6b7. Report an issue: GitHub.