Back to blog
ROS2Tooling

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

José González

Co-Founder

·5 min read

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.

bash
# 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:

bash
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 anything

The 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