First successful Linux field test (NixOS 26.05, Plasma 6, KiCad 10). Getting there peeled three layers the error messages misattribute: the FHS env itself (libgthread), then libs the xcb-cursor hint hides (the xcb-util family, and libzstd for libQt6Core - in zstd.out, not zstd's default output), and finally Plasma's QT_PLUGIN_PATH feeding the system Qt's platform plugins to the wheel's bundled Qt, a private-ABI mismatch that fails exactly like a missing library. New docs/NIXOS.md carries the verified buildFHSEnv wrapper, the steam-run quick test (now with env -u QT_PLUGIN_PATH), and a guide for chasing future missing sonames. README upgrades Linux from untested to works and links the doc. The in-plugin error no longer suggests programs.nix-ld: the venv python is a Nix binary, so the nix-ld shim never engages for it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5.3 KiB
Running Fill Resistance on NixOS
KiCad builds the plugin a private venv from pip wheels
(requirements.txt). Those Linux wheels — PySide6 in particular —
dlopen system libraries at standard FHS paths (/usr/lib), which
NixOS does not provide. Nothing inside the venv can fix that; KiCad
must be launched in an environment that supplies the libraries.
This page gives a verified, declarative setup (field-tested on
NixOS 26.05, KiCad 10, Plasma 6 on Wayland) and maps each failure
mode to its cause, because the error messages are misleading.
The three failure layers
You will hit these in order; each fix below removes one.
libgthread-2.0.so.0: cannot open shared object file(shown in the plugin's own error figure) — no FHS environment at all. PySide6's bundled Qt cannot load glib & friends.qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed…/no Qt platform plugin could be initialized— FHS present but incomplete. Beyond the obviousxcb-util-cursor, the bundled Qt needs the whole xcb-util family (icccm,image,keysyms,render-util,util) andlibzstd(a hard dependency oflibQt6Core; note thatpkgs.zstd's default output ships only the CLI — the library lives inzstd.out). Qt prints the xcb-cursor hint for any missing dependency of the platform plugin, so don't trust it literally.Could not load the Qt platform plugin "wayland"/"xcb" in "" even though it was foundwith all libraries present — the desktop session (KDE Plasma does this) exportsQT_PLUGIN_PATHpointing at the system's Qt plugin directories. That variable takes precedence over the wheel's bundled plugins, so the venv's PySide6 (its own Qt, e.g. 6.11.1) tries to load platform plugins built against the system Qt (e.g. 6.11.0) — a private-ABI mismatch that fails exactly like a missing library. The fix is to unsetQT_PLUGIN_PATHfor KiCad and everything it spawns. KiCad itself is wxWidgets/GTK and does not use the variable.
Recommended setup: an FHS wrapper
Wrap KiCad in buildFHSEnv so a plain kicad launch carries
everything. Home-manager example (home.nix); for a system-wide
install put the same package in environment.systemPackages in
configuration.nix instead:
{ pkgs, ... }:
let
kicad-fhs = pkgs.buildFHSEnv {
name = "kicad";
targetPkgs = p: with p; [
kicad glib fontconfig freetype dbus libGL libxkbcommon
xcb-util-cursor wayland zlib zstd.out
xorg.libX11 xorg.libxcb xorg.libXext xorg.libXrender
xorg.libSM xorg.libICE xorg.libXrandr xorg.libXi
xorg.libXcursor xorg.libXfixes
# xcb-util family needed by PySide6's bundled xcb platform plugin
xorg.xcbutil xorg.xcbutilwm xorg.xcbutilimage
xorg.xcbutilkeysyms xorg.xcbutilrenderutil
];
# Plasma exports QT_PLUGIN_PATH (system Qt plugin dirs); the venv's
# bundled Qt chokes on those mismatched plugins. KiCad is wx/GTK,
# so dropping the variable is safe.
profile = "unset QT_PLUGIN_PATH";
runScript = "kicad";
};
in
{
home.packages = [ kicad-fhs /* replaces bare pkgs.kicad */ ];
# buildFHSEnv ships no .desktop file; restore the menu launcher.
xdg.desktopEntries.kicad = {
name = "KiCad";
exec = "kicad %F";
icon = "kicad";
categories = [ "Development" "Electronics" ];
mimeType = [ "application/x-kicad-project" ];
};
}
Rebuild, fully quit any running KiCad (a running instance keeps
its old environment), relaunch, click the Ω button. A one-line
Could not load the Qt platform plugin "wayland" warning may remain
if the wayland client stack is unhappy — it is harmless; Qt falls
back to xcb (XWayland) and the dialog appears.
Quick test without a rebuild
steam-run provides a broad FHS that is one library short
(libxcb-cursor, required by Qt ≥ 6.5) and, on Plasma, still leaks
QT_PLUGIN_PATH:
XCBCUR=$(nix build --no-link --print-out-paths nixpkgs#xcb-util-cursor)
env -u QT_PLUGIN_PATH QT_QPA_PLATFORM=xcb \
LD_LIBRARY_PATH=$XCBCUR/lib steam-run kicad
(Fish: set XCBCUR (nix build --no-link --print-out-paths nixpkgs#xcb-util-cursor), then the same env … line.)
Debugging further failures
If a new wheel version needs another library, reproduce the plugin's
Qt startup outside KiCad against the wrapper's library set — the
venv survives at
~/.cache/kicad/10.0/python-environments/th.co.b4l.fill-resistance:
ROOTFS=$(nix-store -qR "$(readlink -f "$(command -v kicad)")" \
| grep fhsenv-rootfs | head -1)
env -i HOME=$HOME DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY \
LD_LIBRARY_PATH=$ROOTFS/usr/lib64 QT_DEBUG_PLUGINS=1 \
~/.cache/kicad/10.0/python-environments/th.co.b4l.fill-resistance/bin/python3 \
-c 'from PySide6.QtWidgets import QApplication; \
print(QApplication([]).platformName())'
A missing library shows up as a plain ImportError: libfoo.so.N: cannot open shared object file — map the soname to its nixpkgs
attribute (nix-locate libfoo.so.N, from nix-index) and add it to
targetPkgs. If instead every library loads and only the platform
plugin fails, compare the environment of the running KiCad
(tr '\0' '\n' < /proc/$(pgrep -x kicad)/environ) for Qt variables
leaking in from the session — QT_PLUGIN_PATH above was found
exactly this way.