Skip to content

Linux AppImage: bundled libsoup-3.0/libnghttp2 shadow system libs on non-Debian distros, breaking system GStreamer WebRTC plugins #3494

Open

Description

@scottsanpedro

#2176 (make Linux AppImage GStreamer work on non-Debian distros) removed the bundled
GStreamer plugins so the system ones load — thank you, 0.5.0 now launches natively on
Arch. But two bundled libraries remain that undo that fix for the WebRTC half:

  • The AppImage bundles usr/lib/libsoup-3.0.so.0 (Ubuntu's build). On Arch, the system
    libgstnice.so / libgstwebrtc.so link against system libgupnp-1.6, which is built
    against libsoup 3.6.x. With the bundled (older) libsoup first on the loader path, the
    plugin scanner fails both plugins:

    Failed to load plugin '/usr/lib/gstreamer-1.0/libgstnice.so':
    /usr/lib/libgupnp-1.6.so.0: undefined symbol: soup_server_message_pause
    (identical for libgstwebrtc.so)
    

    → no webrtcbin → huddles have no media pipeline.

  • Renaming the bundled libsoup exposes the second one: system libsoup 3.6.6 needs a
    newer libnghttp2 than the bundled usr/lib/libnghttp2.so.14:

    symbol lookup error: /usr/lib/libsoup-3.0.so.0:
    undefined symbol: nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation
    

Workaround (verified): rename both bundled libs so the system copies load:

cd squashfs-root/usr/lib
mv libsoup-3.0.so.0   libsoup-3.0.so.0.bak
mv libnghttp2.so.14   libnghttp2.so.14.bak

After this (and clearing ~/.cache/gstreamer-1.0/registry.x86_64.bin — the scanner
caches blacklisted plugins), a full rescan shows libgstwebrtc/libgstnice loading
cleanly; the only remaining failures are optional codecs (openexr, spandsp) with no
huddle impact. App runs stably with system libsoup (soname-compatible).

Suggested fix: drop libsoup-3.0.so.0 and libnghttp2.so.14 from the AppImage
(same rationale as removing bundled libsystemd in #2353 and bundled GStreamer in
#2176) — the bundled WebKit is happy with the system copies, soname is stable across
both.

Environment: Buzz Desktop 0.5.0 AppImage (extracted, AppRun), Arch Linux
(EndeavourOS), webkit2gtk-4.1 2.52.5 (system), gstreamer 1.28.5, libsoup3 3.6.6,
gupnp 1.6.10, libnice 0.1.23, PipeWire 1.6.8.


Activity

  1. scottsanpedro commented on Jul 29, 2026

    @scottsanpedro
    Author

    Companion permission-layer issue (the remaining blocker once this is fixed): #3495

  2. scottsanpedro commented on Jul 29, 2026

    @scottsanpedro
    Author

    Still present in 0.5.1 — the two bundled libs are byte-identical to the 0.5.0 ones:

    lib sha256 (first 16) 0.5.0 0.5.1
    usr/lib/libsoup-3.0.so.0 2cb93d9fb132840a ✔ ✔
    usr/lib/libnghttp2.so.14 5696eaac037a57e6 ✔ ✔

    (checked by --appimage-extract-ing usr/lib/libsoup* / usr/lib/libnghttp2* straight out of a freshly downloaded Buzz_0.5.1_amd64.AppImage, so it is not a stale local copy.)

    The rename workaround from the original report still applies unchanged and still works on 0.5.1 — I have it scripted now so it re-applies on every update, since a fresh extract silently reintroduces the problem and the symptom (huddles with no webrtcbin) gives no hint as to why.

    Environment as before, current system versions: Arch, libsoup3 3.6.6-2, libnghttp2 1.69.0-1, gstreamer/gst-plugins-bad 1.28.5-4, libnice 0.1.23-1, gupnp 1.6.10-1, webkit2gtk-4.1 2.52.5-2.

  3. balter-io commented on Jul 30, 2026

    @balter-io

    For anyone hitting this on a current release: the fix from #3607 merged the day after v0.5.2 was cut, so every released build (≤ 0.5.2) still fails with the NotAllowedError. Until the next release ships, here's the workaround I've been running with — a tiny GTK3 module that auto-allows WebKit's user-media/device-info permission requests in the Buzz process.

    It works by adding a GObject emission hook on WebKitWebView::permission-request and calling webkit_permission_request_allow() for WebKitUserMediaPermissionRequest / WebKitDeviceInfoPermissionRequest only. WebKit permission requests honour a single decision, so the early allow makes the unhandled-signal default deny a no-op. All webkit symbols are resolved via dlsym, so it builds without webkit2gtk headers and binds to whatever webkit/glib the app actually loaded (works fine against the AppImage's bundled webkit). If the symbols aren't there it logs and deactivates itself.

    buzz-webkit-mic-shim.c
    /*
     * buzz-webkit-mic-shim — auto-allow WebKitGTK media permission requests in Buzz.
     */
    
    #include <glib-object.h>
    #include <gmodule.h>
    #include <dlfcn.h>
    #include <stdio.h>
    #include <string.h>
    
    typedef GType (*get_type_fn)(void);
    typedef void (*allow_fn)(gpointer);
    
    static allow_fn perm_allow;
    
    static gboolean
    permission_hook(GSignalInvocationHint *ihint, guint n_param_values,
                    const GValue *param_values, gpointer data)
    {
      (void)ihint;
      (void)data;
    
      if (n_param_values < 2)
        return TRUE;
    
      gpointer request = g_value_get_object(&param_values[1]);
      if (!request)
        return TRUE;
    
      const char *tname = G_OBJECT_TYPE_NAME(request);
      if (tname && (strcmp(tname, "WebKitUserMediaPermissionRequest") == 0
                    || strcmp(tname, "WebKitDeviceInfoPermissionRequest") == 0)) {
        perm_allow(request);
        fprintf(stderr, "[buzz-mic-shim] allowed %s\n", tname);
      }
    
      return TRUE; /* keep the hook installed */
    }
    
    G_MODULE_EXPORT void
    gtk_module_init(gint *argc, gchar ***argv)
    {
      (void)argc;
      (void)argv;
    
      get_type_fn wv_get_type =
          (get_type_fn)dlsym(RTLD_DEFAULT, "webkit_web_view_get_type");
      perm_allow = (allow_fn)dlsym(RTLD_DEFAULT, "webkit_permission_request_allow");
    
      if (!wv_get_type || !perm_allow) {
        fprintf(stderr, "[buzz-mic-shim] webkit symbols not found; shim inactive\n");
        return;
      }
    
      GType webview_type = wv_get_type();
      /* Signals are registered in class_init; force it before the lookup. */
      gpointer klass = g_type_class_ref(webview_type);
      guint signal_id = g_signal_lookup("permission-request", webview_type);
      if (signal_id == 0) {
        fprintf(stderr, "[buzz-mic-shim] permission-request signal not found\n");
        g_type_class_unref(klass);
        return;
      }
    
      g_signal_add_emission_hook(signal_id, 0, permission_hook, NULL, NULL);
      /* klass ref intentionally kept for process lifetime. */
      fprintf(stderr, "[buzz-mic-shim] installed permission-request hook\n");
    }

    Build (no webkit headers needed — deliberately no -l libs so glib symbols bind at load time to the glib already in the process):

    gcc -shared -fPIC -O2 -Wall -Wextra \
        $(pkg-config --cflags glib-2.0 gobject-2.0 gmodule-2.0) \
        buzz-webkit-mic-shim.c -o ~/.local/lib/buzz-webkit-mic-shim.so

    Load it by setting GTK3_MODULES for the Buzz process, e.g.:

    GTK3_MODULES=$HOME/.local/lib/buzz-webkit-mic-shim.so ./Buzz_0.5.2_amd64.AppImage

    (For an extracted-AppImage install you can add that export to apprun-hooks/linuxdeploy-plugin-gtk.sh instead.)

    On a successful start you'll see [buzz-mic-shim] installed permission-request hook on stderr, and huddle mic capture works normally after that.

    Two caveats:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions