This error often arrives after compilation has succeeded, which makes it feel oddly late. BitBake is protecting you from a quieter failure: a recipe installed files into its staging root, but those files would vanish before the image is assembled because no output package owns them.
What “installed” and “shipped” mean in BitBake
do_installcopies artifacts into${D}, a destination root used while building the recipe—not directly into the final image.do_packagewalks that staged tree and assigns paths to packages listed inPACKAGES.Each output package has matching rules in
FILES:<package>.The first package in
PACKAGESwhoseFILESrules match a path claims it.Only generated packages selected through image/package dependencies can reach the final root filesystem.
Read the QA message as a manifest diff
bitbake example 2>&1 | tee example-build.log
grep -A20 "installed but not shipped" example-build.logERROR: example-1.0-r0 do_package: QA Issue: example: Files/directories were installed but not shipped in any package:
/opt/example
/opt/example/example.conf
[installed-vs-shipped]Those paths are target paths, not host paths
The message names paths relative to the future target root filesystem.
A directory can be reported along with its children; decide ownership at the most useful package boundary.
The recipe name before the message is not necessarily the final output package name.
Fix all listed artifacts; solving only the first line usually exposes the remainder on the next run.
Keep the complete log because the task path and recipe version help locate
${WORKDIR}.
1. Inspect what do_install actually staged
bitbake -e example | grep -E '^(D|WORKDIR|PACKAGES)='
bitbake -c install -f example
find "$(bitbake -e example | sed -n 's/^D="\(.*\)"/\1/p')" -mindepth 1 -printf '%P\n' | sortThe evaluated environment prints the staging root and package list; the final command lists paths created beneath `${D}`.Start with evaluated metadata, not guesses
bitbake -eshows the final value after includes, classes, overrides, and appends are applied.-c install -freruns the install task and dependent packaging tasks may need rebuilding afterward.${D}can vary with recipe, machine, tune, multilib, and build configuration.The quoted path protects whitespace and prevents accidental shell expansion.
Inspect
temp/log.do_installwhen the staged tree contains files you did not expect.
2. Assign a wanted artifact to the main package
do_install() {
install -d ${D}${datadir}/example
install -m 0644 ${WORKDIR}/example.conf \
${D}${datadir}/example/example.conf
}
FILES:${PN} += "${datadir}/example/example.conf"The install path and package path use different roots
Use
${D}${datadir}while installing because the task writes into the staging root.Use
${datadir}/...inFILES:${PN}because packaging rules describe target paths; adding${D}there is a common mistake.install -dcreates the destination with controlled permissions.Mode
0644is appropriate for a non-executable data/config example; choose permissions based on actual use.Modern override syntax is
FILES:${PN}; older release branches usedFILES_${PN}. Follow the syntax supported by the project’s pinned BitBake version.
3. Create a deliberate subpackage
PACKAGES =+ "${PN}-tools"
FILES:${PN}-tools = "${libexecdir}/example/*"
RDEPENDS:${PN}-tools += "bash"Package ordering decides ownership
=+prepends the tools package so it is considered before the main package.A path matching more than one
FILESrule goes to the earliest package inPACKAGES.Name the exact output package in
FILESand runtime dependency overrides.Add
RDEPENDSonly for genuine runtime requirements; a shell-script shebang is one example.A produced subpackage is not automatically installed in an image—add it through the image recipe, package group, or a justified runtime dependency.
4. Remove build-only or accidental files
do_install:append() {
rm -f ${D}${libdir}/example/*.la
rmdir --ignore-fail-on-non-empty ${D}${libdir}/example
}Deletion should be precise and explainable
Remove an artifact only after confirming the target never needs it.
Use paths rooted beneath
${D}and avoid broad recursive deletion.rm -ftolerates a release where no matching file exists; ensure that flexibility does not conceal an upstream layout change.rmdirremoves only an empty directory, making it safer than recursive removal.When possible, disable installation through the upstream project’s supported build option instead of cleaning up afterward.
5. Repackage and inspect the result
bitbake -c package -f example
oe-pkgdata-util list-pkgs | grep '^example'
oe-pkgdata-util list-pkg-files example
oe-pkgdata-util find-pkg '/usr/share/example/example.conf'The package task completes without installed-vs-shipped; pkgdata identifies which generated package owns each target path.Risk level: caution. Review the command before running it.
A clean task is only the first proof
Forcing a task changes build outputs but does not erase downloads or the entire shared-state cache.
list-pkg-filesverifies package contents, which is stronger evidence than reading the recipe alone.find-pkgresolves a target path to its owning package when pkgdata exists.Use the actual output package name, not automatically the recipe filename.
Build the consuming image afterward and verify both its manifest and target filesystem.
Inspect package-split during a stubborn failure
workdir=$(bitbake -e example | sed -n 's/^WORKDIR="\(.*\)"/\1/p')
find "$workdir/packages-split" -mindepth 1 -maxdepth 4 -printf '%P\n' | sort | lessThe tree shows each output package directory and the paths BitBake assigned to it.This exposes rule overlap and empty packages
packages-split/<package>/mirrors the files assigned to that package.An unexpected owner usually means an earlier package has a broader matching rule.
An absent directory may mean
do_packagedid not rerun after metadata changed.Empty packages are normally not emitted unless
ALLOW_EMPTYrequests them.Work directories are disposable build artifacts; never treat them as deployment output.
Why wildcard fixes can age badly
FILES:${PN} += "/opt/example"can intentionally claim a whole tree, but it may also absorb future plugins, secrets, debug data, or development files.Use standard path variables such as
${bindir},${libdir},${sysconfdir}, and${datadir}instead of hard-coded/usrlayouts.Keep headers, unversioned link libraries, static archives, debug symbols, and documentation in their conventional packages unless product policy says otherwise.
Never use
${D}insideFILESvalues.Review package contents after upstream version bumps because install layouts can change without recipe syntax changing.
Common causes mapped to corrections
Custom `/opt` or `/srv` tree: add a precise
FILES:<package>target path or move the artifact to a standard directory.Empty directory: remove it if unnecessary, or package it only when runtime software genuinely requires it.
Upstream installs tests/examples: disable that install option, remove the files deliberately, or create a clearly named optional package.
Plugin directory: create a plugin subpackage/dynamic packaging policy and verify dependency ownership.
Wrong variable expansion: compare
bitbake -evalues and keep${D}only on the install side.Old underscore overrides copied forward: convert to colon overrides when the project release requires modern syntax.
One file lands in the wrong subpackage: inspect
PACKAGESorder and overlappingFILESglobs.Error returns after an upgrade: diff
log.do_install,${D}, and package contents across the old and new upstream versions.
A compact decision path
Copy the complete list of reported target paths.
Locate the install command or class that creates each path.
Decide whether each artifact belongs on the target at all.
For wanted files, choose the main package or a meaningful subpackage and add a precise
FILESrule.For unwanted files, stop their installation or remove them safely beneath
${D}.Rerun packaging and inspect pkgdata/package-split ownership.
Build the image and confirm its manifest and runtime filesystem.
Version and migration notes
Current Yocto documentation uses colon override syntax such as
FILES:${PN}anddo_install:append.Historical recipes may correctly use
FILES_${PN}anddo_install_appendon old, pinned releases.Do not mechanically modernize syntax without matching the BitBake version used by every layer.
Keep all layers on compatible branches and validate migration notes when upgrading.
The underlying ownership rule—installed beneath
${D}, then claimed by an output package—remains the important mental model.
Verification checklist before merging
No installed-vs-shipped QA message remains without an approved explanation.
Every installed artifact has an intentional package owner.
No
${D}prefix appears inFILESvalues.Output package names, package order, runtime dependencies, permissions, and conffile behavior are reviewed.
Package contents and image manifest match the product requirement.
Clean CI and the supported Yocto branch reproduce the result.
Broad QA suppression and accidental wildcard ownership are absent.
Authoritative references
Yocto QA checks defines
installed-vs-shippedand its two proper resolutions.Yocto variables glossary: FILES documents package-specific target paths, globs, and standard path variables.
Yocto variables glossary: PACKAGES explains output packages and first-match ownership.
Working with packages covers package splitting and package-data inspection.
Comments and corrections