Skip to content

Debugging Builds#

This guide covers how to debug conda package builds with rattler-build when things go wrong. It's designed for both humans and AI agents working with recipes.

Debugging Workflow#

Suppose you have a recipe that fails to build:

recipe.yaml
package:
  name: test
  version: "1.0"

build:
  script:
    - exit 1

Running rattler-build build will fail. When a build fails, the build directory is automatically preserved so you can investigate. There are multiple things you can do to investigate

Enter the Debug Shell#

Jump straight into the failed build environment:

rattler-build debug shell

This opens an interactive shell in the work directory with the build environment loaded. All environment variables ($PREFIX, $BUILD_PREFIX, etc.) are set up exactly as they were during the build.

Now you can modify files and run individual commands to isolate the issue:

./configure --prefix=$PREFIX
make VERBOSE=1
make install

Re-run the Build Script#

Use debug run to re-execute the build script with the full environment already loaded:

# Re-run the build script
rattler-build debug run

# Re-run with shell tracing (bash -x) for verbose output
rattler-build debug run --trace

You can find the working directory by running the following:

rattler-build debug workdir

Modify files inside that directory and run rattler-build debug run to check whether that fixed the problem.

Modify Dependencies#

If you need additional packages in the host or build environment, you can add them without re-running the full setup:

# Add packages to the host environment
rattler-build debug host-add libfoo libbar

# Add build tools
rattler-build debug build-add gdb valgrind

Remember to add them to your recipe.yaml once you found the right set of dependencies.

Create a Patch for Fixes#

After fixing issues in the source code:

# Create a patch from your changes
rattler-build debug create-patch \
  --directory . \
  --name my-fix \
  --exclude "*.o,*.so,*.pyc"

# Preview what would be included
rattler-build debug create-patch \
  --directory . \
  --name my-fix \
  --dry-run

To include new files:

rattler-build debug create-patch \
  --directory . \
  --name my-fix \
  --add "*.txt,src/new_file.c"

In the end, you will have a patch file that you can include in your recipe

Update Recipe and Rebuild#

Add the patch to your recipe:

source:
  - url: https://example.com/source.tar.gz
    sha256: ...
    patches:
      # this needs to be manually added
      - my-fix.patch

Then rebuild:

rattler-build build --recipe recipe.yaml

Debugging a Successful Build#

If your recipe builds successfully but you still want to inspect the environment, use --keep-build to prevent cleanup:

rattler-build build --recipe recipe.yaml --keep-build
rattler-build debug shell

Setting Up a Debug Environment Without Building#

If you want to prepare a debug environment without running the build script at all, use debug setup. This resolves dependencies, downloads sources, and creates the build script — but doesn't execute it:

rattler-build debug setup --recipe recipe.yaml
rattler-build debug shell

This is useful when you want to inspect or modify sources before running the build for the first time.

Inspecting and Extracting Packages#

The rattler-build package subcommand provides utilities for inspecting and extracting built packages, which is useful for debugging package contents.

Inspecting Packages#

Use package inspect to view package metadata without extracting:

# Basic package information
rattler-build package inspect mypackage-1.0-h12345.conda

# Show all information including file listing
rattler-build package inspect mypackage-1.0-h12345.conda --all

# Show specific sections
rattler-build package inspect mypackage-1.0-h12345.conda --paths      # File listing with hashes
rattler-build package inspect mypackage-1.0-h12345.conda --about      # Extended about info
rattler-build package inspect mypackage-1.0-h12345.conda --run-exports # Run exports

# Output as JSON for scripting
rattler-build package inspect mypackage-1.0-h12345.conda --json

Extracting Packages#

Use package extract to extract a package to a directory for inspection:

# Extract to a directory named after the package
rattler-build package extract mypackage-1.0-h12345.conda

# Extract to a custom destination
rattler-build package extract mypackage-1.0-h12345.conda -d my-extracted

# Extract directly from a URL (supports authenticated channels)
rattler-build package extract https://conda.anaconda.org/conda-forge/linux-64/python-3.11.0-h12345.conda

After extraction, the command reports the SHA256/MD5 checksums and file size, which is useful for verifying package integrity.

Both .conda and .tar.bz2 package formats are supported.

Build Directory Structure#

When rattler-build builds a package, it creates:

output/
└─ rattler-build-log.txt            # Append-only log of build directories (latest at bottom)
└─ bld/                             # Build directories
│   └─ rattler-build_<name>_<timestamp>/
│       └─ work/                    # Source code and working directory
│       │   └─ .source_info.json    # Source information (extracted folders, etc.)
│       │   └─ build_env.sh         # Environment setup script
│       │   └─ conda_build.sh       # The actual build script (sources `build_env.sh`)
│       │   └─ conda_build.log      # Complete build output
│       └─ host_env_placehold_.../  # Host environment (runtime dependencies)
│       └─ build_env/               # Build environment (build-time dependencies)
└─ src_cache/                       # Downloaded and extracted sources
└─ build_cache/                     # Staging cache (experimental)
│   └─ staging_<sha256>/            # Per-staging-output cache
│       └─ metadata.json            # Cache metadata (deps, sources, variant)
│       └─ prefix/                  # Cached prefix files from staging build
│       └─ work_dir/                # Cached work directory from staging build
└─ <platform>/                      # Built packages

Environment Variables Available in the Debug Shell#

Inside the debug shell, you have access to:

Variable Description
$PREFIX Host prefix (where packages get installed)
$BUILD_PREFIX Build prefix (tools for building)
$SRC_DIR Source directory (same as work directory)
$RATTLER_BUILD_DIRECTORIES Full JSON with all directory info
$RATTLER_BUILD_RECIPE_PATH Path to the recipe file
$RATTLER_BUILD_RECIPE_DIR Directory containing the recipe
$RATTLER_BUILD_BUILD_DIR The build directory root
$RATTLER_BUILD_HOST_PREFIX Path to the host prefix
$RATTLER_BUILD_BUILD_PREFIX Path to the build prefix

Common Debugging Scenarios#

Compilation Failures#

# Re-run the build script with tracing to see where it fails
rattler-build debug run --trace

# Or enter the shell and run specific build commands
rattler-build debug shell
make VERBOSE=1
cmake --build . --verbose

Missing Files or Dependencies#

# Check source information
cat .source_info.json | jq .

# List what's in the work directory
find . -type f | head -30

# Check what's in the environments
ls $PREFIX/lib/
ls $BUILD_PREFIX/bin/

# Add a missing dependency on the fly
rattler-build debug host-add libmissing

Library Not Found Errors#

# Check if the library exists in PREFIX
find $PREFIX -name "lib*.so*" -o -name "lib*.dylib*"

# Check pkg-config paths
echo $PKG_CONFIG_PATH
pkg-config --libs --cflags libfoo

Build Log Analysis#

All build output is saved to conda_build.log:

# View the full log
less conda_build.log

# Search for errors
grep -i error conda_build.log
grep -i "undefined reference" conda_build.log

Debugging with AI Agents#

The debug subcommands are designed to work well with AI coding agents (Claude Code, Codex, etc.) that cannot use interactive shells. The key principle is: set up once, then iterate fast by re-running the build script.

Agent Workflow#

# 1. Set up the debug environment (slow, only once)
rattler-build debug setup --recipe recipe.yaml

# 2. Get the work directory
rattler-build debug workdir

# 3. Edit source files in work directory to fix the issue
#    (agent edits files directly)

# 4. Re-run the build script (fast — no dependency resolution)
rattler-build debug run

# 5. If the build fails, go back to step 3
# 6. Once it works, create a patch
rattler-build debug create-patch --name my-fix

Key Points for Agents#

  • debug setup is non-interactive — it sets up everything and exits. Use this instead of debug shell which opens an interactive shell.
  • debug workdir prints the work directory path to stdout — no jq or log parsing needed.
  • debug run re-runs the build script with the environment already set up. Use --trace for verbose bash -x output. This is the fast inner loop — it takes seconds, not minutes, because dependencies are already installed.
  • debug host-add / debug build-add let agents add missing dependencies without re-running the full setup.
  • debug create-patch generates a unified diff from changes in the work directory. The agent can then add the patch to the recipe.

Parsing the Build Log#

The last line of output/rattler-build-log.txt is JSON:

{
  "work_dir": "/path/to/output/bld/rattler-build_pkg_1234/work",
  "build_dir": "/path/to/output/bld/rattler-build_pkg_1234",
  "host_prefix": "/path/to/output/bld/rattler-build_pkg_1234/host_env_placehold_...",
  "build_prefix": "/path/to/output/bld/rattler-build_pkg_1234/build_env",
  "recipe_dir": "/path/to/recipe/dir",
  "recipe_path": "/path/to/recipe.yaml",
  "output_dir": "/path/to/output"
}

Understanding Relocatability#

rattler-build makes packages relocatable through:

  1. RPATH patching - Changes .dylib and .so files to use relative paths ($ORIGIN, @loader_path) using patchelf or install_name_tool

  2. Placeholder replacement - At install time, replaces placeholder strings in binaries and text files with the actual prefix

Important: The placeholder is a long string (placehold_placehol_...). If your code has small buffer optimization or assumes static string lengths for file paths, you may need to adjust it. The $PREFIX length will differ at installation time. The placeholder replacement in binary files will overwrite the placeholder string, move the remainder until \0 is found in the original string, and pad with \0 bytes.

Useful Commands Reference#

# Build commands
rattler-build build --recipe recipe.yaml --keep-build
rattler-build build --recipe recipe.yaml --channel conda-forge --no-test

# Debug commands
rattler-build debug setup --recipe recipe.yaml         # Set up environment
rattler-build debug shell                              # Open shell in last build
rattler-build debug shell --work-dir /path/to/work     # Open shell in specific build
rattler-build debug workdir                            # Print work directory path
rattler-build debug run                                # Re-run build script
rattler-build debug run --trace                        # Re-run with bash -x tracing
rattler-build debug host-add python numpy              # Add packages to host env
rattler-build debug build-add cmake                    # Add packages to build env
rattler-build debug create-patch --name fix            # Create patch from changes
rattler-build debug create-patch --name fix --dry-run  # Preview patch

# Test commands
rattler-build test --package-file output/linux-64/mypackage-1.0.tar.bz2