name: nix-debug
description: Deep debugging of Nix expressions, evaluation, and derivations
allowed-tools:
- Bash
- Read
Nix Deep Debugging
nix eval - Complete Reference
# Basic
nix eval .#attr # Evaluate attribute
nix eval .#attr --json # JSON output
nix eval .#attr --raw # Raw string output
# Apply transformation
nix eval .#attr --apply 'x: x.meta'
nix eval .#attr --apply 'builtins.attrNames'
# Arbitrary expressions
nix eval --expr '1 + 1'
nix eval --expr 'builtins.attrNames (import ./. {})'
nix eval --expr --impure 'builtins.getEnv "HOME"'
# Remote flakes
nix eval nixpkgs#hello.meta
nix eval github:NixOS/nixpkgs#lib.version
# Debug flags
nix eval .#attr --show-trace # Full stack trace
nix eval .#attr --debugger # Interactive debugger
nix eval .#attr -vvvvv # Maximum verbosity
nix repl - Interactive Debugging
# Start with flake
nix repl --expr 'builtins.getFlake (toString ./.)'
nix repl .# # Nix 2.19+
# Start with nixpkgs
nix repl '<nixpkgs>'
nix repl --expr 'import <nixpkgs> {}'
REPL Commands
:? Help
:p EXPR Pretty print (shows structure)
:t EXPR Show type
:b DRV Build derivation
:l PATH Load nix file
:lf FLAKE Load flake
:r Reload all files
:q Quit
:doc FUNC Show documentation (Nix 2.10+)
REPL Introspection Patterns
# List all attributes
builtins.attrNames someSet
# Recursive attribute listing
lib.attrsets.mapAttrsRecursive (path: value: path) someSet
# Check what's in a module
builtins.attrNames config.services
# Find where option is defined
options.services.nginx.enable.definitionsWithLocations
# Function signature
builtins.functionArgs someFunction
# Returns: { arg1 = false; arg2 = true; }
# true = has default value
# Get source location
builtins.unsafeGetAttrPos "enable" options.services.nginx
# Returns: { file = "/path"; line = 42; column = 5; }
Built-in Debug Functions
builtins.trace
# Print message, return value unchanged
builtins.trace "checkpoint reached" someValue
# Print with value
builtins.trace "value is: ${toString x}" x
# Conditional trace
builtins.trace (if debug then "debug: ${msg}" else "") value
lib.debug Functions
# Print and return value
lib.debug.traceVal expr
# Prints: trace: <value>
# Returns: <value>
# Deep evaluate then print
lib.debug.traceValSeq expr
# Print first, return second
lib.debug.traceSeq exprToPrint exprToReturn
# Trace with label
lib.debug.traceValFn (x: "myVar = ${toString x}") myVar
# Trace attribute set keys
lib.debug.traceSeqN 2 someSet result # depth 2
Debugging Assertions
# Show what failed
assert lib.assertMsg (x > 0) "x must be positive, got: ${toString x}";
result
# Warn without failing
lib.warn "deprecated option used" value
lib.warnIf condition "message" value
Interactive Debugger (Nix 2.9+)
nix eval .#attr --debugger
Debugger Commands
:bt Show backtrace
:env Show current environment
:st Step into
:s Step over
:c Continue
:q Quit debugger
Trigger Debugger Programmatically
builtins.break value # Breaks here when --debugger is used
nix-instantiate - Deep Evaluation
# Strict evaluation (force everything)
nix-instantiate --eval --strict -E 'import ./.'
# Parse only (syntax check)
nix-instantiate --parse ./file.nix
# Show what would be built
nix-instantiate ./default.nix
# Evaluate specific attribute
nix-instantiate --eval -A attrPath ./file.nix
Derivation Debugging
# Show derivation structure
nix derivation show .#package
# Show build log
nix log .#package
nix log /nix/store/<hash>-name
# Why does A depend on B?
nix why-depends .#package .#dependency
# Path info
nix path-info -rsSh .#package # recursive, size, human
nix path-info --json .#package | jq
Error Diagnosis Cookbook
"infinite recursion encountered"
# 1. Get trace
nix eval .#attr --show-trace 2>&1 | tail -100
# 2. Look for repeated file/line
# 3. Check for:
# - Module importing itself
# - config.x referencing config.x
# - Overlays referencing final before defined
"attribute 'x' missing"
# List available attributes
nix eval .#someSet --apply builtins.attrNames
# In REPL
:p builtins.attrNames someSet
# Check if it exists
nix eval --expr 'someSet ? x'
"called with unexpected argument 'x'"
# Check expected arguments
nix eval --expr 'builtins.functionArgs someFn'
# Shows: { expectedArg1 = false; expectedArg2 = true; }
"cannot coerce X to string"
# Problem: ${nonString}
# Solutions:
toString value # for numbers, paths
builtins.toJSON value # for complex values
lib.generators.toPretty {} value # pretty print
"is not a function"
# Check type
nix eval --expr 'builtins.typeOf problemValue'
# Common cause: forgot to import
# Wrong: pkgs.callPackage ./pkg.nix
# Right: pkgs.callPackage ./pkg.nix {}
Module System Debugging
# See final option value
nix eval .#nixosConfigurations.host.config.services.nginx.enable
# See option definition locations
nix eval .#nixosConfigurations.host.options.services.nginx.enable.files
# See all definitions
nix eval .#nixosConfigurations.host.options.services.nginx.enable.definitionsWithLocations --json | jq
# Check if option exists
nix eval --expr '.#nixosConfigurations.host.options.services ? nginx'
Performance Debugging
# Time evaluation
time nix eval .#attr --json >/dev/null 2>&1
# Verbose timing
NIX_SHOW_STATS=1 nix eval .#attr
# Find slow imports (add traces)
nix eval --expr '
builtins.trace "before import" (
let x = import ./.;
in builtins.trace "after import" x
)
'
Quick Debug Snippets
# Print current file/line
builtins.trace "at ${__curPos.file}:${toString __curPos.line}" value
# Dump entire set structure
builtins.trace (builtins.toJSON (builtins.mapAttrs (k: v: builtins.typeOf v) someSet)) result
# Assert with helpful message
assert builtins.isAttrs x || throw "Expected set, got ${builtins.typeOf x}";