colcon Exited 0. Your ROS2 Package Is Still Missing. Here's Why.
You run `colcon build`. It finishes. Exit code 0. You source the workspace, run your launch file, and get `package 'my_package' not found`. You build again. Same result. The build says it worked. The

José González
Co-Founder
You run colcon build. It finishes. Exit code 0. You source the workspace, run your launch file, and get package 'my_package' not found. You build again. Same result. The build says it worked. The install step failed without telling you. This failure mode has burned hours on ROS2 projects, and it keeps happening because the symptoms look like a runtime problem when they are a build problem.
What "Exited 0" Actually Means
colcon is a meta-build tool. It orchestrates CMake, ament, and Python setuptools across your workspace. Exit code 0 means colcon finished its orchestration without crashing. It does not mean every package installed correctly. It does not mean your workspace is in a consistent state. It means the process ended.
The disconnect happens in the install step. colcon builds your package in a build directory, then runs an install step to copy artifacts into the install directory. If that step fails silently — and it can — the build directory looks fine, the log shows no error, and the package simply does not exist where ROS2 expects it.
The Three Silent Failure Modes
Three specific issues cause this. Each has shown up on GitHub issues and ROS Discourse. None of them produces an error at default verbosity.
Workspace overlay conflicts. If you source a system ROS2 installation and then build in an overlay workspace, the ament index can get confused about which package belongs to which layer. The overlay builds successfully, but the ament index points to the underlay version — or to nothing — depending on environment variable ordering. Running colcon build --symlink-install makes this worse because symlinks into the build directory break when the build directory is cleaned.
Python package discovery edge cases. setuptools discovers packages by directory structure. If your setup.py uses find_packages() and you have a nested directory that looks like a package but is not, setuptools installs the wrong module tree. The package name exists in the install directory. The actual entry point does not. ros2 run my_package my_node fails with No executable found, even though ros2 pkg list shows the package.
ament index corruption. The ament resource index is a set of marker files in install/share/ament_index that ROS2 uses to resolve packages at runtime. If these markers are missing, stale, or point to deleted directories, the package exists on disk but is invisible to ros2 pkg list. This happens after incomplete clean builds, after switching branches that change package names, and after manual rm -rf build/ without cleaning the install directory.
How to Diagnose It
When a package is missing after a successful build, do not run colcon build again. Check these three things first.
# 1. Does the package exist in the install directory?
ls install/share/ | grep my_package
# 2. Is the ament index entry present?
ls install/share/ament_index/resource_index/packages/ | grep my_package
# 3. Is the entry point actually installed?
ls install/lib/my_package/If step 1 fails, the install step did not run for that package. Check build/my_package/CMakeFiles/CMakeError.log and build/my_package/colcon-*.log for warnings that did not surface as errors.
If step 2 fails but step 1 succeeds, the ament index is out of sync. This is the most common silent failure. Clean the install directory and rebuild: rm -rf build/ install/ log/ && colcon build --symlink-install.
If step 3 fails, the Python package structure is wrong. Check that your setup.py includes the correct packages and that your source directory has an __init__.py in every package directory.
The Setuptools Version Trap
There is a related failure that does produce an error, but the error message is useless. colcon has a documented dependency on setuptools <= 58.2.0. If your environment has a newer version — which is the default on recent Ubuntu and in most modern Python environments — colcon build may fail with an error that does not mention setuptools at all.
The fix is pinning: pip install setuptools==58.2.0. Then add it to every requirements file, every Dockerfile, every CI pipeline. The pin is annoying but workable. The real issue is that a build tool cares about your application's Python environment at all. It shouldn't. When it does, you carry that constraint across every machine, every Docker image, and every new hire's first day.
The Habit That Prevents It
Experienced ROS2 developers follow the same routine after every build, especially in a new environment or after a dependency change:
cd ~/ros2_ws
rm -rf build/ install/ log/
cd src/ament_index && git status # confirm no uncommitted changes
cd ~/ros2_ws
cd src && vcs pull # update dependencies
cd ~/ros2_ws
cd build
cd ~/ros2_ws
colcon build --symlink-install
source install/setup.bash
ros2 pkg list | grep my_package # verify before running anythingThe ros2 pkg list verification takes two seconds. It saves hours of debugging runtime errors that are actually install errors in disguise.
What to Take Away
You should be able to trust your build output. ROS2 doesn't always let you. The stack has three layers — colcon, ament, setuptools — and each fails differently. Some failures are loud. Some are silent. The runtime error you debug for two hours often started as an install step that said nothing was wrong.
After every build in a new environment, spend ten seconds checking: is the package in the install directory? Is the ament index entry there? Does ros2 pkg list see it? That is the difference between catching the problem now or tracing package not found through launch files and environment variables two hours later.
Continue reading
Related Posts
Python ROS2 Nodes Start Fine. Then the Callbacks Start Drifting.
Most ROS2 teams start in Python. The API is cleaner. Iteration is faster. rclpy handles the majority of nodes without issue. Then at some point a timer fires late, a high-frequency publisher drops mes
May 26, 2026
ROS2 on Mac or Windows Means Docker. That Sentence Costs More Than Most Teams Realize.
ROS2 is Linux-native. That is not a complaint about the design choice — Linux is the right foundation for production robotics. The complaint is about what the development toolchain assumes: that every
May 26, 2026
Your ROS2 Subscriber Is Not Receiving Messages. No Error. No Warning. Nothing.
Your publisher is running. `ros2 topic echo` shows data flowing out. Your subscriber node is alive. The callback never fires. No error in the log. No warning on the console. Nothing. This is a QoS mis
May 26, 2026