Nix Deep Debugging

SkillDev tools

Deep debugging of Nix expressions, evaluation, and derivations

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Nix Deep Debugging skill

What this skill tells your AI

The instructions your AI receives, as published by r17x/universe in .claude/skills/nix/debug/SKILL.md and read by ahel’s review.

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}";

Signals

GitHub stars
135
Forks
12
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
nix-debug
Source
github.com/r17x/universe