Debugging Yocto Build Failures: A Field Guide
The official Yocto Project documentation includes a debugging chapter 1 that covers logs, variable inspection, dependency graphs, and parallel make races. It is thorough and worth reading. But it did not prepare me for the real failures — the ones that cost hours, recur across releases, and have no obvious search result on Stack Overflow.
This article is a running log of failures I hit while learning Yocto. Each entry follows the same format: what the error looked like, what caused it, how I fixed it, and how to avoid it next time.
I am not a Yocto expert. These notes exist because I kept hitting the same problems and wanted a reference. The examples come from building an x86-64 embedded Linux image across a couple of Yocto releases.
1. Pseudo abort during do_install
Symptom: The build fails on a recipe's do_install task with pseudo abort in the log. The error message mentions a path and a database corruption
message.
Root cause: Pseudo is Yocto's fakeroot implementation — it intercepts
filesystem calls to emulate root privileges during packaging 2. Its SQLite
database lives under tmp/work/<arch>/<recipe>/<version>/pseudo/. When two
builds run concurrently against the same work directory, or when a previous
build was killed mid-task, the pseudo database can become corrupt.
Fix: Delete the pseudo database for that recipe and rebuild:
rm -rf build/tmp/work/<arch>/<recipe>/<version>/pseudo
bitbake <recipe>Do not delete the entire tmp/work directory — that forces a full rebuild of
the recipe. Only the pseudo/ subdirectory is needed.
Prevention: Always let builds complete. If you must interrupt a build,
use Ctrl-C once and wait for BitBake to finish its current task. The -k
(continue) flag reduces the damage from individual recipe failures.
2. LIC_FILES_CHKSUM mismatch after layer update
Symptom: After updating a layer's pinned commit, a recipe fails with a
LIC_FILES_CHKSUM mismatch. The expected and actual checksums are printed.
Root cause: The upstream recipe changed its license text, copyright year,
or formatting 3. The checksum is a hash of the license file referenced in
LIC_FILES_CHKSUM. When the source changes, the hash changes.
Fix: Update the checksum in the recipe or bbappend:
LIC_FILES_CHKSUM = "file://LICENSE;md5=<new-hash>"Run md5sum on the extracted license file to get the new hash. The extracted
source lives under tmp/work/<arch>/<recipe>/<version>/.
For proprietary firmware or closed-source components, use:
LICENSE = "CLOSED"
LIC_FILES_CHKSUM = ""Prevention: After updating layer commits, run a build with -k to
surface all LIC_FILES_CHKSUM failures at once, then fix them in batch.
3. CMake FetchContent fails in offline builds
Symptom: A recipe using CMake's FetchContent module fails during
do_configure or do_compile with network errors, even though the source
was already fetched.
Root cause: Yocto's cmake bbclass sets the CMake variable
FETCHCONTENT_FULLY_DISCONNECTED to force offline builds 4. However,
some upstream projects explicitly enable FetchContent downloads by
re-defining this variable in their CMakeLists.txt. When this happens,
the recipe still tries to download dependencies at configure time, which
fails because the build has no network access. This is common with recipes
that bundle dependencies via FetchContent rather than using system
libraries.
Fix: In the bbappend, explicitly disable FetchContent downloads:
EXTRA_OECMAKE:append = " -DFETCHCONTENT_FULLY_DISCONNECTED=ON"If the dependency is genuinely needed, add it as a separate Yocto recipe and
make this recipe DEPENDS on it, or vendor the dependency source into the
recipe's SRC_URI.
Prevention: When adding a new CMake-based recipe, check the top-level
CMakeLists.txt for FetchContent_Declare or FetchContent_MakeAvailable
calls. If present, plan for the disconnection strategy before the first build.
4. CMake generator mismatch: Ninja vs Makefiles
Symptom: A recipe fails during do_compile with errors about missing
Ninja or the CMake cache referencing a different generator than the one
currently in use.
Root cause: By default, Yocto's cmake bbclass uses Ninja as the build
generator 5. Some upstream projects hardcode the Unix Makefiles generator
in their CMakeCache.txt or have build scripts that invoke make directly.
Additionally, nested CMake invocations (e.g., a top-level project that calls
ExternalProject_Add) may use a different generator than the parent.
Fix: Force the Makefiles generator for the affected recipe:
OECMAKE_GENERATOR = "Unix Makefiles"For stale CMake caches after a generator change, clean the recipe and rebuild:
bitbake -c cleansstate <recipe>
bitbake <recipe>Prevention: When a recipe's native build system calls make directly or
uses nested CMake projects, set the generator to Unix Makefiles from the
start to avoid the rebuild.
5. Kernel module symbol lookup errors at runtime
Symptom: A kernel module compiles successfully but fails to load on the
target with Unknown symbol or disagrees about version of symbol errors.
Root cause: Out-of-tree kernel modules must be compiled against the exact
kernel headers that match the running kernel. If the recipe inherits the
module class but does not set KERNEL_DIR correctly, the module is compiled
against the host kernel headers instead of the target kernel 6.
Fix: Ensure the recipe inherits module and points to the correct kernel
source:
inherit module
KERNEL_DIR = "${STAGING_KERNEL_DIR}"If the module uses a non-standard build system, verify that the Makefile
references $(KERNEL_SRC) or $(KERNEL_DIR) rather than a hardcoded path.
A note on KERNEL_DIR vs KERNEL_SRC: The Yocto module class passes
KERNEL_SRC=${STAGING_KERNEL_DIR} to the module's Makefile, not KERNEL_DIR.
Setting KERNEL_DIR in your recipe affects BitBake variable resolution but
the actual environment variable the Makefile sees is KERNEL_SRC. If your
module's build system reads $(KERNEL_SRC), it will automatically pick up
the correct path. If it reads a different variable, you may need a custom
do_compile override.
Prevention: After building a kernel module, inspect the module's vermagic
with modinfo <module>.ko and compare it against the target kernel's
uname -r. They must match exactly.
6. Kernel API drift: removed functions between kernel versions
Symptom: An out-of-tree kernel module that compiled successfully on kernel 6.6 fails to compile on kernel 6.18 with errors about undefined functions or changed function signatures.
Root cause: The Linux kernel removes and renames internal APIs between
releases. A module that calls del_timer_sync() on kernel 6.6 will fail on
6.18 because that function was removed in favor of timer_delete_sync().
Fix: Add a sed fixup in do_configure:prepend to patch the source before
compilation:
do_configure:prepend() {
sed -i 's/del_timer_sync/timer_delete_sync/g' ${S}/src/timer.c
}For larger API changes, carry a proper patch in the recipe's files/
directory and apply it via SRC_URI.
Prevention: Before migrating to a new kernel major version, grep the
module source for known-deprecated APIs. The kernel's
Documentation/process/deprecated.rst and LWN's API change summaries are
good resources.
7. TMPDIR sanity check false positives
Symptom: After moving your build directory or cloning a new checkout,
BitBake refuses to start with a TMPDIR has changed or TMPDIR sanity check
error.
Root cause: BitBake stores a record of the previous TMPDIR path in
build/conf/sanity_info and build/conf/saved_tmpdir. If you move the build
directory or copy it to a different path, BitBake detects the mismatch and
halts 7.
Fix: Delete the stale sanity files:
rm -f build/conf/sanity_info build/conf/saved_tmpdirBitBake will regenerate them with the current path on the next build.
Prevention: Use relative paths in your build setup where possible, or always build from a fixed path. If you need to move builds, script the cleanup of these two files.
8. Sstate cache corruption
Symptom: A recipe that previously built successfully now fails with
unexplained errors — linker errors, missing headers, or corrupted binaries.
Rebuilding the same recipe with -c cleansstate fixes it.
Root cause: The shared state cache (sstate-cache/) stores pre-built
task outputs to avoid redundant work 8. If a task's output is corrupted
(e.g., due to disk I/O error, interrupted build, or a time-of-check to
time-of-use race with concurrent builds), the corrupted artifact is reused
indefinitely.
Fix: Clean the sstate cache and rebuild from scratch:
rm -rf build/sstate-cache
bitbake <recipe>For a targeted fix, identify the specific task's sstate artifact and delete only that one. To find which task produced the corrupted output, compare the failing task's signature against a known-good build:
# Dump the task signature from a known-good build
bitbake-dumpsig tmp/stamps/<arch>/<recipe>/<version>.do_compile.sigdata.<hash>
# Compare against the failing build's signature to see what diverged
bitbake-diffsigs \
tmp/stamps/<arch>/<recipe>/<version>.do_compile.sigdata.<good-hash> \
tmp/stamps/<arch>/<recipe>/<version>.do_compile.sigdata.<bad-hash>The diff output shows which variable changed between builds, which usually points to the source of the corruption.
Prevention: Do not share a single sstate cache directory across concurrent builds from different checkouts. For CI, isolate sstate caches per branch or use a shared read-only mirror with a local write-through cache.
9. C++ ABI mismatch: static constexpr and out-of-line definitions
Symptom: A C++ library compiles and links, but at runtime binaries that
depend on it fail with symbol lookup error or undefined symbol for what
appear to be trivial constants.
Root cause: In C++14 mode, static constexpr class members require
out-of-line definitions in a .cpp file if they are odr-used (e.g., passed
by reference). The compiler may optimize them away at the call site in C++17
mode, where the rules changed. If the library is built with C++14 and the
consumer with C++17, the consumer expects a definition that does not exist.
Fix: Force the library to build with C++17:
EXTRA_OECMAKE:append = " -DCMAKE_CXX_STANDARD=17"Or, for autotools-based projects:
CXXFLAGS:append = " -std=c++17"Prevention: Standardize on a single C++ standard across all layers. If you must mix standards, document which recipes use which standard and why.
10. Layer compatibility: LAYERSERIES_COMPAT mismatch
Symptom: After adding a layer or updating to a new Yocto release, the
build fails with a LAYERSERIES_COMPAT error stating that the layer is not
compatible with the current release series.
Root cause: Each Yocto release has a codename (Scarthgap, Styhead, etc.).
Layers declare which release series they support via LAYERSERIES_COMPAT in
their layer.conf 9. If a layer only lists scarthgap and you are
building with a newer release like styhead, BitBake rejects it.
Fix: Update the layer's conf/layer.conf to include the new release
series:
LAYERSERIES_COMPAT_meta-custom = "scarthgap styhead"Only add series that you have verified the layer actually works with. Adding a release codename without testing is the fastest route to a different class of failures.
Prevention: Before starting a Yocto release migration, audit every
layer's LAYERSERIES_COMPAT line. Upstream layers that have not yet added
support for the new release need either a backport or a local compatibility
patch.
11. Recipe version mismatch after repository restructure
Symptom: After switching from the monolithic poky repository to
standalone openembedded-core + bitbake + meta-yocto, recipes that
used to build fine now fail with Nothing PROVIDES errors.
Root cause: Some setups use standalone openembedded-core + bitbake
directly rather than the poky convenience repository, and switching
between the two layouts can break bblayers.conf. The poky repo still
exists as a convenience wrapper 10, but if your bblayers.conf was
written for the standalone layout (or vice versa), layer paths and included
layers won't match.
Fix: Update bblayers.conf to match the repository layout you are using.
For the standalone layout:
BBLAYERS ?= " \
${TOPDIR}/../openembedded-core/meta \
${TOPDIR}/../meta-yocto/meta-poky \
${TOPDIR}/../meta-yocto/meta-yocto-bsp \
"Prevention: Know which layout your project uses and document it. When
cloning a new checkout, verify that the paths in bblayers.conf actually
exist on disk before running BitBake.
12. BitBake shell function syntax errors
Symptom: A bbappend or recipe with a shell function fails to parse with a cryptic syntax error pointing to a seemingly correct line.
Root cause: BitBake shell functions follow specific syntax rules.
Common violations: a space before parentheses in function definitions
(myfunc () instead of myfunc()), using $ for variable dereference
inside shell functions when BitBake-style ${} is needed, or missing
closing braces on inline Python blocks 11.
Fix: For function definition spacing:
# WRONG — space before parens
do_configure:prepend () {
...
}
# CORRECT
do_configure:prepend() {
...
}For BitBake variable references inside shell functions:
# WRONG inside a shell function
echo $WORKDIR
# CORRECT
echo ${WORKDIR}Prevention: Run bitbake -p (parse only) before a full build to catch
syntax errors early. It parses all recipes and reports syntax errors without
starting any task execution.
Where these cluster
Looking back at my notes, these twelve failures fall into four buckets:
| Category | Failures | Debugging approach |
|---|---|---|
| Build environment state | Pseudo abort, sstate corruption, TMPDIR sanity, CMake cache | Delete the specific corrupted artifact, not the entire build |
| Layer & release compatibility | LAYERSERIES_COMPAT, LIC_FILES_CHKSUM, version mismatch, repo restructure | Audit layers before upgrading; apply fixes in batch |
| Toolchain & ABI | Kernel API drift, C++ standard mismatch, kernel module vermagic | Compare kernel versions and compiler flags explicitly |
| Recipe authoring | CMake FetchContent, generator mismatch, shell syntax | Check the upstream build system before writing the recipe |
When I migrated between Yocto releases, failures showed up in all four categories at once. The thing I learned: fix each category independently rather than applying random workarounds and hoping the build completes.
References
[1] Yocto Project, "Debugging Build Failures," https://docs.yoctoproject.org/dev/dev-manual/debugging.html, accessed June 2026.
[2] Yocto Project, "Pseudo," https://www.yoctoproject.org/software-item/pseudo/, accessed June 2026.
[3] Yocto Project, "Maintaining Open Source License Compliance During Your Product's Lifecycle," https://docs.yoctoproject.org/dev/dev-manual/licenses.html, accessed June 2026.
[4] Kitware, "CMake FetchContent Module," https://cmake.org/cmake/help/latest/module/FetchContent.html, accessed June 2026.
[5] Yocto Project, "Classes — cmake," https://docs.yoctoproject.org/ref-manual/classes.html#ref-classes-cmake, accessed June 2026.
[6] Yocto Project, "Incorporating Out-of-Tree Modules," https://docs.yoctoproject.org/kernel-dev/advanced.html#incorporating-out-of-tree-modules, accessed June 2026.
[7] Yocto Project, "Build Directory," https://docs.yoctoproject.org/ref-manual/structure.html#the-build-directory-build, accessed June 2026.
[8] Yocto Project, "Shared State Cache," https://docs.yoctoproject.org/overview-manual/concepts.html#shared-state-cache, accessed June 2026.
[9] Yocto Project, "Understanding and Creating Layers," https://docs.yoctoproject.org/dev/dev-manual/layers.html, accessed June 2026.
[10] Yocto Project, "Moving to the Yocto Project 5.1 Release (Styhead)," https://docs.yoctoproject.org/migration-guides/migration-5.1.html, accessed June 2026.
[11] BitBake, "BitBake User Manual — Syntax and Operators," https://docs.yoctoproject.org/bitbake/2.18/bitbake-user-manual/bitbake-user-manual-metadata.html, accessed June 2026.