CMake “Could NOT find …”: find_package Failures

Key takeaways

Fix CMake Could NOT find Boost and other packages: Config vs Module mode, dev packages on Linux, version constraints, COMPONENTS, case sensitivity, and package managers.

CMake guides: find_package · CMake overview · build system basics · targets. For other failures, see common CMake errors.

Introduction: “Could NOT find Boost (missing: …)”

You called find_package(Boost REQUIRED) and CMake stopped with Could NOT find Boost or missing variables. The library may be installed—but CMake cannot locate its CMake package files or the compiled components you need.

The key shift in thinking is that find_package does not search for the library itself. It searches for a small CMake script that describes the library: where its headers and binaries are, which targets it provides, and what they depend on. A library can be fully installed and still be invisible to CMake if that script is missing, sits in a directory CMake does not search, or rejects the request (wrong version, wrong architecture, missing component). Reading the error with that in mind tells you what to fix. The full message usually lists the files it looked for, for example Could not find a package configuration file provided by "fmt" with any of the following names: fmtConfig.cmake fmt-config.cmake, followed by the hint to set CMAKE_PREFIX_PATH or fmt_DIR.

This article covers:

  • Config vs Module search
  • Five frequent causes: not installed, wrong path, version, missing Config, case, missing COMPONENTS
  • CMAKE_PREFIX_PATH
  • vcpkg and Conan
flowchart TD
  A[find_package fails] --> B[Installed?]
  B --> C[Config/Module path]
  C --> D[CMAKE_PREFIX_PATH]
  D --> E[Version & components]
  E --> F[vcpkg / Conan]

How find_package works

Config mode (preferred when available): loads FooConfig.cmake (or similar) shipped with the library—defines imported targets like Boost::system. Module mode: uses CMake’s FindFoo.cmake—works for many common libs but can lag or guess wrong paths. Search locations include CMAKE_PREFIX_PATH, Foo_DIR, and system prefixes.

By default, find_package(Foo) tries Module mode first: it looks for FindFoo.cmake in CMAKE_MODULE_PATH and in CMake’s own module directory, and only if none exists does it switch to Config mode and look for FooConfig.cmake or foo-config.cmake. find_package(Foo CONFIG REQUIRED) skips Module mode entirely, which gives clearer errors when you know the package ships a config file. The two modes also produce different results: Config packages define imported targets such as fmt::fmt that carry include paths, compile definitions and dependencies with them, while older Find modules often only set variables like FOO_INCLUDE_DIRS and FOO_LIBRARIES that you have to wire up by hand.

Boost is the classic example of the transition. Boost 1.70 and later ship BoostConfig.cmake, and CMake 3.30 removed its bundled FindBoost module (policy CMP0167), so on current systems Boost is found in Config mode. Older tutorials that set BOOST_ROOT or Boost_USE_STATIC_LIBS were written for FindBoost; some of these variables are still honored by Boost’s config files, but not all.

Once a package is found, its result is cached in CMakeCache.txt (as Foo_DIR, or as NOTFOUND). Changing the environment afterwards does not trigger a new search: after installing a missing package, delete the build directory, or at least the cache entry, and configure again. Forgetting this is the most common reason a fix “does not work”.


Not installed / path

Linux: runtime-only packages lack headers—install -dev / lib-all-dev* metapackages when needed.

ls /usr/include/boost/
ls /usr/lib/x86_64-linux-gnu/libboost_system*

macOS: brew install boost then often set:

export CMAKE_PREFIX_PATH="$(brew --prefix boost)"

On Debian and Ubuntu, a library is split into a runtime package (libfmt9) with only the .so file and a development package (libfmt-dev) with headers, the unversioned .so symlink used for linking, and the CMake config files. Programs that merely run need only the first; building needs the second. dpkg -L libfmt-dev | grep cmake shows where its config files went, which also answers “where should CMake look?”. On Fedora and RHEL the suffix is -devel. Homebrew installs into a prefix that CMake usually searches automatically on Apple Silicon (/opt/homebrew), but keg-only formulae such as openssl@3, llvm or qt@5 are deliberately not linked into it, which is why they need an explicit CMAKE_PREFIX_PATH or OPENSSL_ROOT_DIR.


Version mismatch

find_package(Boost 1.75 REQUIRED)

If only 1.71 is installed, either lower the requirement, upgrade Boost, or find_package(Boost REQUIRED) without a hard minimum if acceptable.

In Config mode, the version check is performed by the package’s own FooConfigVersion.cmake, and packages choose their compatibility rule. Many use SameMajorVersion, so asking for version 2.0 rejects an installed 3.1 even though it is newer, and the error lists it as unsuitable: The following configuration files were considered but not accepted: /usr/lib/cmake/Foo/FooConfig.cmake, version: 3.1.0. The same mechanism rejects a package built for a different architecture, typically a 32-bit build found by a 64-bit project on Windows. C++ libraries also carry ABI concerns that the version number alone does not capture, such as the compiler and standard library they were built with, which is part of why package managers that build everything with one toolchain are so useful. Only raise a minimum version when you actually use a feature that requires it.


No Config / Module fails

Point CMake at the config directory:

set(MyLib_DIR "/path/to/lib/cmake/MyLib")
find_package(MyLib REQUIRED)

Or fall back to find_library / find_path and target_include_directories + target_link_libraries.

Setting MyLib_DIR inside CMakeLists.txt works but hard-codes one machine’s layout into the project; passing -DMyLib_DIR=... on the command line, or putting it in a CMakeUserPresets.json that is not committed, keeps the project portable. The value must be the directory that contains MyLibConfig.cmake, not the install prefix.

The find_library/find_path fallback is the right tool for libraries that ship no CMake support at all. If you go that way, wrap the result in an imported target once, so the rest of the project can link against MyLib::MyLib as if the package had a config file:

find_path(MYLIB_INCLUDE_DIR mylib.h PATHS /opt/mylib/include)
find_library(MYLIB_LIBRARY NAMES mylib PATHS /opt/mylib/lib)
add_library(MyLib::MyLib UNKNOWN IMPORTED)
set_target_properties(MyLib::MyLib PROPERTIES
    IMPORTED_LOCATION "${MYLIB_LIBRARY}"
    INTERFACE_INCLUDE_DIRECTORIES "${MYLIB_INCLUDE_DIR}")

Putting this in a cmake/FindMyLib.cmake and adding that directory to CMAKE_MODULE_PATH makes find_package(MyLib) work in Module mode, which is how the standard Find modules are written.


Case sensitivity

On Linux/macOS, find_package(boost) may fail where find_package(Boost) succeeds—match the exact package name from upstream docs.

The reason is how CMake turns the name into file names. For find_package(Boost) it looks for BoostConfig.cmake and boost-config.cmake (the second form is always lowercase), and for Module mode FindBoost.cmake. For find_package(boost) it looks for boostConfig.cmake and boost-config.cmake, so whether it works depends on which spelling the package installed and on whether the file system is case-sensitive. That is why the same CMakeLists.txt can configure on a developer’s Mac or Windows machine (case-insensitive by default) and fail on Linux CI. The spelling also determines the result variables: find_package(boost) sets boost_FOUND, not Boost_FOUND, so later checks can silently read an unset variable. Imported target names such as Boost::headers are defined by the package and are case-sensitive everywhere. Copy the exact name from the library’s documentation or from the name of its config file.


COMPONENTS (Boost example)

Header-only parts need no link, but Boost.System, Boost.Filesystem, etc. need compiled libs:

find_package(Boost REQUIRED COMPONENTS system filesystem)
target_link_libraries(myapp PRIVATE Boost::system Boost::filesystem)

Without COMPONENTS, you may get headers but LNK2019 at link time.

Components let one package expose several libraries, and find_package succeeds only if every requested component is found; the error names the missing one (Could NOT find Boost (missing: filesystem)). A missing component usually means the library was built or installed without it: a distribution’s split packages (libboost-filesystem-dev), a vcpkg feature that was not enabled, or a Boost built from source with --with-libraries limited. (Boost.System itself has been header-only since 1.69, and Boost::system is kept as an interface target for compatibility, so the classic failures today are components like filesystem, program_options, thread or regex.) The link error on the other side looks like LNK2019: unresolved external symbol with MSVC or undefined reference to 'boost::filesystem::...' with GCC and Clang, and it means the target was not linked even though the headers were found. Linking the imported targets, rather than variables like ${Boost_LIBRARIES}, also brings in their include directories and transitive dependencies automatically.


CMAKE_PREFIX_PATH

cmake -DCMAKE_PREFIX_PATH=/opt/mylibs ..
list(APPEND CMAKE_PREFIX_PATH "/opt/mylibs")
find_package(MyLib REQUIRED)

CMAKE_PREFIX_PATH is a list of install prefixes, the directories that contain include/, lib/ and share/. CMake appends the standard subdirectories itself (lib/cmake/<name>*, share/<name>*, and a few others), so pointing it at /opt/mylibs/lib/cmake/MyLib does not work, while /opt/mylibs does. Several prefixes are separated by ; in CMake (-DCMAKE_PREFIX_PATH="/opt/a;/opt/b"), and the environment variable of the same name uses the platform’s path separator (: on Unix). The command-line or preset form is preferable to appending in CMakeLists.txt, for the same portability reason as with _DIR. Unlike <Pkg>_DIR, one prefix helps CMake find every package installed under it, which is why it is the usual answer for a custom install location.


vcpkg & Conan

vcpkg (toolchain-driven):

cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake ..

Conan (CMakeDeps/CMakeToolchain):

conan install . --build=missing
cmake --preset conan-default

Both package managers work by making their install tree visible to find_package, and both fail in the same characteristic way when that step is skipped: the package is installed, and CMake still reports “Could NOT find”. With vcpkg, the toolchain file must be given on the first configure of a build directory; adding it later to an existing cache has no effect, so delete the build directory and configure again. In manifest mode (a vcpkg.json in the project), the toolchain also installs the listed dependencies during configure. After installing a port, vcpkg prints the exact find_package and target_link_libraries lines to use, which is worth copying, since package names and target names do not always match the port name. With Conan 2, conan install generates <Pkg>-config.cmake files and a CMakePresets.json in the output folder; the cmake --preset step picks up the generated toolchain, and the build type used in conan install (-s build_type=Debug) has to match the one you build, or the config files for the other configuration are not found.

A subtler failure is common on machines that also have system packages installed: CMake quietly picks up the system version of one library and the vcpkg version of another, and the build fails later with link errors or ABI mismatches rather than “Could NOT find”. Printing the found location (message(STATUS "fmt from ${fmt_DIR}")) after each find_package makes such mixes visible immediately.


Debugging workflow

  1. Read which variables are “missing” in the error.
  2. Confirm dev packages / vcpkg packages installed.
  3. Locate *Config.cmake.
  4. Set CMAKE_PREFIX_PATH or -DFoo_DIR=….
  5. Run cmake —debug-find .. to print search traces.

--debug-find (CMake 3.17+) is the most useful step and the one most often skipped. It prints, for every find_package, the list of prefixes and directories searched in order, which answers “did CMake even look where the file is?” in one run. To limit the output to one package, use --debug-find-pkg=Boost (CMake 3.23+). If the file is in a searched directory and still rejected, the output of the normal configure run includes the “considered but not accepted” list with the reason, usually a version or architecture mismatch.


Summary table

SymptomAction
Headers missingInstall -dev / use vcpkg
Wrong pathCMAKE_PREFIX_PATH
VersionRelax requirement or upgrade
No Configfind_library fallback
CaseMatch find_package spelling
Link still failsAdd COMPONENTS + target_link_libraries


Closing: Could NOT find almost always means CMake cannot see the package root—set CMAKE_PREFIX_PATH, install dev artifacts, specify COMPONENTS, and prefer vcpkg/Conan for repeatable Windows/Linux/macOS workflows.


Frequently Asked Questions (FAQ)

Q. I set CMAKE_PREFIX_PATH and find_package still fails. What should I check?

A. CMAKE_PREFIX_PATH should point at the install prefix (the directory that contains lib/cmake/<Pkg>/ or share/<Pkg>/), not at the folder with the Config file itself; for that folder, set <Pkg>_DIR instead. Also check the <Pkg>ConfigVersion.cmake file: a package built for a different architecture (for example 32-bit vs 64-bit) or with an incompatible version is rejected, and CMake lists such candidates as unsuitable in the error output. Running with --debug-find shows exactly which paths were searched.