← Back to Blog

Debugging Yocto Build Failures: A Field Guide

·12 min read
YoctoBitBakeEmbedded LinuxDebuggingBuild Systems

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_tmpdir

BitBake 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:

CategoryFailuresDebugging approach
Build environment statePseudo abort, sstate corruption, TMPDIR sanity, CMake cacheDelete the specific corrupted artifact, not the entire build
Layer & release compatibilityLAYERSERIES_COMPAT, LIC_FILES_CHKSUM, version mismatch, repo restructureAudit layers before upgrading; apply fixes in batch
Toolchain & ABIKernel API drift, C++ standard mismatch, kernel module vermagicCompare kernel versions and compiler flags explicitly
Recipe authoringCMake FetchContent, generator mismatch, shell syntaxCheck 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.