The ld Cannot Find -lpthreads error means the linker can’t locate a library named libpthreads, so you need the right flag, package, or library path.
This message pops up at the link stage, after your code compiles. You can have clean .o files, then the build stops the moment ld tries to satisfy -lpthreads. Most of the time the root cause is a mismatch: your build system asks for a library name that doesn’t exist on your machine, or it exists but the linker can’t see the directory that holds it.
The good news is that this error is mechanical. Once you identify what -l is asking for, you can fix it with a small, repeatable set of checks.
Why The Linker Can’t Find Your Thread Library
The -l option tells the linker to search for a library named libNAME.so or libNAME.a, using default search paths plus any paths you add with -L. That naming rule is standard across many Unix-like toolchains. Red Hat’s GCC developer guide walks through the same idea and also points out that search order matters when you mix -L and -l. (Reference)
So when a build uses -lpthreads, the linker looks for libpthreads.so (or libpthreads.a). On most Linux and macOS systems, the library name you actually want is pthread, not pthreads. That means the correct library switch is typically -lpthread, or better, the compiler driver switch -pthread that sets both compile and link options for POSIX threads.
There are a few other cases that can trigger the same message even when the name is correct, such as missing “-dev” packages that provide the unversioned .so link file, or a library that lives in a nonstandard directory. A common pattern is “I have libfoo.so.1 on disk, but I’m missing the libfoo.so linker name.” That exact pattern shows up in many linker error fixes. (Reference)
Fixing ld Cannot Find -lpthreads In GCC Builds
Start with the smallest change that matches how pthread linking works on your platform. In many projects, the fix is a one-line edit in a Makefile, a CMake file, or a build command.
- Replace -lpthreads With -pthread — In GCC and Clang,
-pthreadis the safest switch for POSIX threads because it configures both compilation and linking in one go. Ask Ubuntu’s pthread thread shows a minimal test where a build fails without it and succeeds with it. (Reference) - Try -lpthread If You Can’t Use -pthread — Some build tools accept only a raw library name. In that case, link with
-lpthreadand remove the stray “s.” - Re-run The Build From A Clean State — Delete the build directory or run the tool’s clean target so you don’t keep linking with cached flags.
If you control the build command yourself, a minimal working compile-and-link line looks like this:
gcc -O2 -Wall -pthread main.c -o app
If your toolchain uses separate compile and link steps, keep -pthread on both. That ensures any needed preprocessor or ABI flags stay in sync.
Thread Linking In CMake
CMake projects often trip over threads when they hard-code a library name. The more reliable pattern is to use CMake’s threads package and link the imported target. That lets CMake pick the right flags for your system. If you maintain the project, check for any explicit -lpthreads in CMakeLists.txt or generated link lines and remove it.
- Use find_package(Threads) — This asks CMake to detect the proper thread flags on the host.
- Link Threads::Threads — This attaches the correct compile and link options without you guessing the flag.
- Inspect The Full Link Command — Build with verbose output so you can see the exact
ldline that contains-lpthreads.
Check The Command Line And Flag Choice
Before you install anything, confirm what your build is actually doing. Many projects hide the final link line behind a build tool wrapper. You want the raw command that calls the compiler driver and then hands off to ld.
- Turn On Verbose Output — In Make, try
make V=1ormake VERBOSE=1. In CMake, usecmake --build . --verbose. You’re looking for the line that contains-lpthreads. - Confirm You’re Using The Compiler Driver — Use
gccorg++for linking, not rawld, unless you truly know what you’re doing. The GCC manual page explains how the driver coordinates compilation and linking stages. (Reference) - Check Flag Ordering — Libraries should come after the object files that need them. If you put thread flags before your objects, the linker can drop them when it tries to resolve symbols.
- Confirm The Name Is Correct — If you see
-lpthreadsanywhere, treat it as suspect. The pthread library is spelled without the trailing “s” in the link name on typical systems.
That Threads target can resolve to different flags depending on the toolchain. On GNU toolchains it often maps to -pthread, which both enables thread-safe compilation settings and adds the right link bits. Ask Ubuntu shows the same pattern when a program fails with missing pthread_create, then works once it is rebuilt with -pthread. (Reference)
If you’re consuming a third-party CMake project, resist the urge to patch it by adding -L paths at random. Fix the library name first, then re-run CMake so it regenerates clean link lines. If the project uses Threads::Threads yet you still see -lpthreads, the string is being injected elsewhere, often via a cached variable, a toolchain file, or a custom script that appends raw linker flags.
If you’re using a third-party build script, hunt down where it injects thread flags. Common spots are LDFLAGS, LIBS, target_link_libraries, or a helper macro that appends libraries at the end of the link line.
Install The Right Packages And Files
When the flag is correct and the error still hits, the next likely cause is missing linker-visible files. On Linux, the unversioned .so file is often shipped by a “-dev” package, while the runtime package ships only the versioned .so.X file. The linker needs the unversioned name at build time.
Threading is part of the system C library on many platforms, so you rarely install a separate “pthread package.” Still, you can hit this issue in a few real situations: a minimal container image, a custom sysroot, a cross-compile toolchain, or a broken symlink created during an upgrade. A Unix & Linux Stack Exchange thread links this error to a stale libpthread.so symlink that points to an old location after a C library update. (Reference)
| What You See | Likely Cause | What To Do |
|---|---|---|
Only libpthread.so.0 exists |
Missing dev link name | Install C library development package for your distro |
| Symlink points to nowhere | Stale symlink after upgrade | Reinstall the package that owns the symlink, then re-run the linker cache |
| Cross build can’t see system libs | Wrong sysroot or search paths | Set --sysroot and add the target library directories with -L |
One extra wrinkle: glibc 2.34 folded libpthread into libc during in-place upgrades. That change means some machines link thread symbols without any extra library flag, while older machines still need a thread flag at link time. Red Hat’s write-up explains why builds can behave differently across glibc versions and why a thread flag is still worth setting for portability. (Reference)
So, even if your box links fine today, patching the build to use -pthread keeps your project steady across distros, containers, and CI runners. The glibc release notes also call out the merge of libpthread into libc as a deliberate change. (Reference)
If you’re on Debian or Ubuntu, the development files come from packages like gcc, make, and libc6-dev. On Fedora or RHEL-like systems, look for glibc-devel. On Alpine, use build-base and the relevant libc development package. Your exact package names depend on your distro and libc choice.
Fix The Search Paths And Build System
Even when a library exists, the linker can miss it if it lives outside default search paths. This is common with custom installs under /usr/local, vendor SDKs, or a project that builds its own static libraries into a build directory.
- Locate The Real Library Files — Run
ldconfig -p | grep pthreadon Linux to see what the dynamic linker cache knows. For static archives, search forlibpthread.ain your sysroot or toolchain path. - Add The Directory With -L — Place
-L/path/to/libsbefore your-loptions, as the Red Hat guide notes for search order. (Reference) - Separate Build-Time And Run-Time Paths —
-Laffects link-time search.LD_LIBRARY_PATHaffects runtime loading. Don’t mix them up when your goal is to fix anlderror. - Stop Editing Symlinks By Hand — If you’re missing the unversioned
.soname, install the correct package instead of creating a manual symlink. A common “cannot find -lNAME” fix is to install the development package that owns the link name. (Reference)
Also check if you’re building with -static. Static linking changes what files the linker needs. Some distros don’t ship static libc archives by default, so a static build can fail even when dynamic linking works.
Quick Verification Before You Rebuild
Once you’ve made a change, verify it with a tiny thread test. This keeps you from chasing a bigger build when the real problem is still present.
- Create A Small Test File — Write a program that calls
pthread_createandpthread_join. - Build With -pthread — Use a single command so you can see what changes fix the link step.
- Confirm The Flag In Your Main Build — Re-run your project build in verbose mode and make sure
-lpthreadsis gone. - Lock The Fix Into The Build Scripts — Patch the Makefile, CMake files, or build config so it stays fixed for the next machine and the next CI run.
When you want an even faster sanity check, compile with -Wl,--verbose or print the compiler’s link specs. GCC documents how to pass linker options through the driver with -Xlinker or -Wl, syntax. (Reference)
After that, rebuild your project. If the next error is an “undefined reference” to a pthread symbol, that’s progress. It means the linker found the thread library name and moved on to symbol resolution, which is a different, simpler step: you either forgot -pthread in one spot or you’re still linking in the wrong order.
As a final check, search your repo for the exact string ld Cannot Find -lpthreads. If it’s hard-coded in docs or scripts, update it so the same typo doesn’t come back six months from now in build scripts too.
If you hit stop in a container, rebuild the image with headers installed. Many slim images keep only files, so the link step fails while the app runs on a full local host system.
