Run Swift on Debian Trixie

Installing Swift toolchain on Debian is a no-brainer. Users can either pick up swiftly or unpack the current tarball, and if you are running a supported Debian version—e.g. Debian 12 Bookworm at the time of this writing—then you are good to go.

This article is about running the latest Swift toolchain on the current stable Debian release: Trixie.

When running swift repl on the current stable Debian release, we usually ended up with an error:

~/.local/share/swiftly/toolchains/6.2.4/usr/bin/lldb: error while loading shared libraries: libpython3.11.so.1.0: cannot open shared object file: No such file or directory

I have been dealing with this error for a while and, up to Swift toolchain version 6.2.4, I would create symbolic links of the libpython files to sort of meet Swift REPL / LLDB requirements:

cd /usr/lib/$(uname -m)-linux-gnu
for f in libpython3.13.* ; do
  ln -s $f $(echo $f | sed s/3\.13/3\.11/)
done

I would also symlink the Python stdlib directory itself — /usr/lib/python3.11/usr/lib/python3.13 — and LLDB was none the wiser. So much for a dependency!

⚠️ Spoiler: this symbolic link comes back to bite in the Troubleshooting section. ⚠️

It worked, until it didn’t

Then came Swift 6.3 with LLDB 21.0:

$ swift repl
PLEASE submit a bug report to  and include the crash backtrace.
Stack dump:
0.      Program arguments: ~/.local/share/swiftly/toolchains/6.3.0/usr/bin/lldb "--repl=-disable-objc-interop -color-diagnostics -Xcc -fcolor-diagnostics -empty-abi-descriptor -no-auto-bridging-header-chaining -in-process-plugin-server-path ~/.local/share/swiftly/toolchains/6.3.0/usr/lib/swift/host/libSwiftInProcPluginServer.so -plugin-path ~/.local/share/swiftly/toolchains/6.3.0/usr/lib/swift/host/plugins -plugin-path ~/.local/share/swiftly/toolchains/6.3.0/usr/local/lib/swift/host/plugins"
 #0 0x00005565426d9928 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (~/.local/share/swiftly/toolchains/6.3.0/usr/bin/lldb+0x2e928)
 #1 0x00005565426d76d5 llvm::sys::RunSignalHandlers() (~/.local/share/swiftly/toolchains/6.3.0/usr/bin/lldb+0x2c6d5)
 #2 0x00005565426da9c1 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x00007ff556a4bdf0 (/lib/x86_64-linux-gnu/libc.so.6+0x3fdf0)
 #4 0x00007ff556aa3bce (/lib/x86_64-linux-gnu/libc.so.6+0x97bce)
 #5 0x00007ff556aa3c29 __pthread_once (/lib/x86_64-linux-gnu/libc.so.6+0x97c29)
 #6 0x00007ff5583d0b2c lldb_private::lldb_initialize_ScriptInterpreterPython() (~/.local/share/swiftly/toolchains/6.3.0/usr/bin/../lib/liblldb.so.21.0+0x11d0b2c)
 #7 0x00007ff557b2b94d lldb_private::SystemInitializerFull::Initialize() (~/.local/share/swiftly/toolchains/6.3.0/usr/bin/../lib/liblldb.so.21.0+0x92b94d)
 #8 0x00007ff557d702f9 lldb_private::SystemLifetimeManager::Initialize(std::unique_ptr<lldb_private::SystemInitializer, std::default_delete<lldb_private::SystemInitializer>>) (~/.local/share/swiftly/toolchains/6.3.0/usr/bin/../lib/liblldb.so.21.0+0xb702f9)
 #9 0x00007ff557a3969f lldb::SBDebugger::InitializeWithErrorHandling() (~/.local/share/swiftly/toolchains/6.3.0/usr/bin/../lib/liblldb.so.21.0+0x83969f)
#10 0x00005565426bf54a main (~/.local/share/swiftly/toolchains/6.3.0/usr/bin/lldb+0x1454a)
#11 0x00007ff556a35ca8 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca8)
#12 0x00007ff556a35d65 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29d65)
#13 0x00005565426bb041 _start (~/.local/share/swiftly/toolchains/6.3.0/usr/bin/lldb+0x10041)

💣 Program crashed: Bad pointer dereference at 0x0000000000000000

Platform: x86_64 Linux (Debian GNU/Linux 13 (trixie))

Thread 0 "lldb" crashed:

  0 0x00007ff556b1a7b9 <unknown> in libc.so.6
... 

Backtrace took 0.00s

The crash is happening in lldb_initialize_ScriptInterpreterPython(): LLDB (which backs the Swift REPL) is trying to load a Python scripting backend and crashing with a null pointer dereference, almost certainly because it was compiled against a specific libpython version that doesn’t exist on Trixie.

A Python C API symbol either moved or changed structure layout between 3.11 and 3.13—and unlike before, LLDB 21.0 is no longer forgiving about it.

I don’t know about you, but I gave up on submitting bugs or enhancement requests to Apple. The most surgical fix is to replace the symbolic links with a stub library1 that exports all Python symbols LLDB needs as no-ops, instead of pointing at a real but ABI-mismatched Python 3.13.

First, we list the symbols liblldb.so.21.0 needs from Python:

nm -D ~/.local/share/swiftly/toolchains/6.3.0/usr/lib/liblldb.so.21.0 \
    | awk '$1 == "U" {print $2}' \
    | grep -E '^_?Py' \
    | awk '{print "void " $1 "() {}"}' \
    > /tmp/pystub.c

Then we build and install the stub:

gcc -shared -fPIC -o /tmp/pystub.so /tmp/pystub.c

sudo cp /tmp/pystub.so "/usr/lib/$(uname -m)-linux-gnu/libpython3.11.so.1.0"

A better approach

It sure is fun to hack all the things but it leaves LLDB’s Python in an undefined state, and that matters: Swift REPL runs on top of LLDB. LLDB uses Python as its scripting engine: breakpoint callbacks, custom commands, and automation all run through it. Let’s do this properly.

The right path is to download the latest Python 3.11—and corresponding libnsl2 dependency—from Debian 12 Bookworm:

export ARCH=$(dpkg --print-architecture)
wget "http://ftp.debian.org/debian/pool/main/p/python3.11/libpython3.11_3.11.2-6+deb12u6_$ARCH.deb"
wget "http://ftp.debian.org/debian/pool/main/p/python3.11/libpython3.11-stdlib_3.11.2-6+deb12u6_$ARCH.deb"
wget "http://ftp.debian.org/debian/pool/main/p/python3.11/libpython3.11-minimal_3.11.2-6+deb12u6_$ARCH.deb"
wget "http://ftp.debian.org/debian/pool/main/libn/libnsl/libnsl2_1.3.0-2_$ARCH.deb"

Now let’s install them:

sudo dpkg -i libnsl2_1.3.0-2_*.deb
sudo dpkg -i libpython3.11-minimal*.deb libpython3.11-stdlib*.deb libpython3.11_*.deb

Just like Python, libnsl2 was also updated in Trixie:

$ apt-cache policy libnsl2
libnsl2:
  Installed: 1.3.0-2
  Candidate: 1.3.0-3+b3
  Version table:
     1.3.0-3+b3 500
        500 http://deb.debian.org/debian trixie/main amd64 Packages
        500 http://mirror.hetzner.com/debian/packages trixie/main amd64 Packages
 *** 1.3.0-2 100
        100 /var/lib/dpkg/status

Trixie has your back and offers to upgrade:

sudo apt update
sudo apt upgrade -y

And voilà 🍾

$ swift repl
Welcome to Swift version 6.3 (swift-6.3-RELEASE).
Type :help for assistance.
  1> print("Hello, World!")
Hello, World!
  2> import Foundation
  3> print(ProcessInfo.processInfo.operatingSystemVersionString)
Debian GNU/Linux 13 (trixie)

Troubleshooting

If you followed the symbolic links trick on an earlier Swift toolchain and left the /usr/lib/python3.11/usr/lib/python3.13 symlink in place, installing the Bookworm packages on top of it is a bad time. dpkg unpacks Bookworm’s 3.11 stdlib through the symlink into the real 3.13 tree, clobbering files both interpreters need. You’ll see python3 crashing on startup with an InteractiveConsole local_exit kwarg error, swift repl dying on an SRE module mismatch, and dpkg -V flagging every .py file in sight.

Remove the symlink and reinstall the 3.13 stack cleanly to put Trixie’s stdlib back where it belongs:

sudo rm /usr/lib/python3.11
sudo apt install --reinstall libpython3.13-minimal libpython3.13-stdlib python3.13-minimal python3.13

Then re-run the Bookworm install from the previous section. With the symlink gone, dpkg creates a real /usr/lib/python3.11/ directory this time, and the two stdlib trees live side by side as they should. python3 and swift repl both come back to life.

Conclusion

Swift officially supports Debian stable. Except when it doesn’t. Debian 13 Trixie was released in August 2025, yet Swift 6.3 only targets Debian 12 Bookworm.

There’s no technical reason a modern compiled language runtime should be entangled with the system Python at all. LLDB’s Python scripting backend is a powerful debugging feature, not a hard runtime requirement for the Swift compiler itself. The fact that shipping for a new distro requires users to either fake a Python ABI or backport packages from the previous release says more about prioritization than complexity.

Hopefully, with Debian 12 Bookworm end of life in June 2026, Swift 6.4 will get there 🤞


  1. A stub library is a shared library that exports the expected symbols but does nothing—it exists purely to satisfy the dynamic linker at load time. ↩︎