Systematic resolution of Nix evaluation failures using error classification, isolation techniques, and targeted fixes
Comprehensive Nix debugging framework that combines error classification, systematic isolation, and targeted resolution patterns. This skill provides both automated tools and manual workflows for resolving the most common Nix evaluation failures.
Core principle: Every evaluation error has a systematic resolution path - panic less, debug more.
Announce at start: "I'm using the Nix Debugging skill to resolve this systematically."
Error Types Covered:
Symptoms that trigger this skill:
error: infinite recursion encounterederror: attribute 'X' missingerror: expression does not evaluate to a functionerror: undefined variable 'X'nix eval or nix flake check failures# Full systematic debugging workflow
./scripts/debug-evaluation.sh .#failingAttribute
# Classify a specific error
./scripts/debug-evaluation.sh --phase triage .#failingAttribute
# Isolate failing component
./scripts/debug-evaluation.sh --phase isolate .#failingAttribute
# Get resolution guidance
./scripts/debug-evaluation.sh --phase resolve .#failingAttribute
# Classify error from command output
nix eval .#failing 2>&1 | ./scripts/classify-error.sh
# Classify error from file
./scripts/classify-error.sh -f error.log
# Verbose classification with guidance
echo "error: infinite recursion encountered" | ./scripts/classify-error.sh -v
Goal: Classify error and gather initial context
# Get full error context with trace
nix eval --show-trace .#failingAttribute
# For flakes: check specific outputs
nix flake check --show-trace
Key Questions:
Automated: ./scripts/debug-evaluation.sh --phase triage TARGET
Goal: Isolate failing expression from full configuration
For Infinite Recursion:
# Isolate problematic definition
nix eval --show-trace --expr '
let
pkgs = import <nixpkgs> {};
# Copy only the failing expression here
in yourFailingExpression
'
# Test with minimal inputs
nix eval --expr 'yourFunction minimalInput'
For Attribute Errors:
# Test attribute path step by step
nix eval --expr '(import ./flake.nix).outputs'
nix eval --expr '(import ./flake.nix).outputs.homeManagerConfigurations'
nix eval --expr '(import ./flake.nix).outputs.homeManagerConfigurations.m4rknewt0n'
Automated: ./scripts/debug-evaluation.sh --phase isolate TARGET
Goal: Apply targeted fixes based on error classification
Pattern 1: Self-referencing let-bindings
# ❌ BAD - infinite recursion
let
system = system;
in system
# ✅ GOOD - use function parameter
{ pkgs, system, ... }:
{
inherit system;
}
Pattern 2: Circular module dependencies
# ❌ BAD - modules reference each other
{
imports = [ ./module-a.nix ./module-b.nix ];
}
# module-a.nix imports module-b, module-b.nix imports module-a
# ✅ GOOD - extract common dependencies
{
imports = [ ./common-deps.nix ./module-a.nix ./module-b.nix ];
}
Pattern 1: Missing flake outputs
# ❌ BAD - attribute not found
nix eval .#nonexistentOutput
# ✅ GOOD - check available outputs first
nix eval .#outputs # List all available
nix eval .#homeManagerConfigurations.m4rknewt0n # Use correct path
Pattern 2: Nested attribute access
# ❌ BAD - assuming structure
config.programs.neovim.plugins
# ✅ GOOD - verify structure exists
builtins.attrNames config.programs.neovim
# or use optional chaining with defaults
config.programs.neovim.plugins or []
Pattern 1: List vs Set confusion
# ❌ BAD - passing list where set expected
[ item1 item2 ] # When function expects { name = ...; }
# ✅ GOOD - correct type
{ name = "item1"; } # or convert list to set
Pattern 2: String vs Path
# ❌ BAD - string interpolation issues
"${./config}" # When ./config is a path
# ✅ GOOD - explicit conversion
toString ./config
# or use path operations
./config + "/file"
File: modules/home/default.nix
# ❌ Common error - wrong import path
imports = [ ./nonexistent-module.nix ];
# ✅ Fix - check actual file structure
ls modules/home/ # Verify file exists
imports = [ ./shared-config.nix ./me.nix ]; # Use correct paths
File: flake.nix
# ❌ Common error - missing homeManagerConfigurations
{
outputs = { self, nixpkgs, home-manager, ... }: {
# Missing homeManagerConfigurations output
packages.x86_64-linux.default = ...;
};
}
# ✅ Fix - add missing output
{
outputs = { self, nixpkgs, home-manager, nixCats, ... }: {
homeManagerConfigurations."m4rknewt0n" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
extraSpecialArgs = { inherit nixCats; };
modules = [ ./configurations/home/m4rknewt0n.nix ];
};
};
}
File: modules/home/me.nix
# ❌ Common error - wrong module signature
{ config, pkgs, ... }: {
# Missing nixCats import
programs.neovim.plugins = with nixCats.plugins; [ ... ];
}
# ✅ Fix - correct module signature
{ config, pkgs, nixCats, ... }: {
programs.neovim.plugins = with nixCats.plugins; [ ... ];
}
# Quick validation during debugging
just check # Run nix flake check
just lint # Run statix for semantic issues
just deep-lint # Run nixd LSP diagnostics
# Find semantic issues that cause evaluation errors
statix check
statix fix # Auto-fix common issues
# Format while debugging to avoid syntax issues
alejandra . # Format all Nix files
# ❌ BAD - assuming NixOS-specific modules
{ config, lib, ... }:
{
# systemd.services only exists on NixOS
systemd.services.my-service = ...;
}
# ✅ GOOD - platform detection
{ config, lib, pkgs, ... }:
{
# Only on NixOS
config.users = if lib.versionAtLeast config.system.nixos.version "23.11"
then { users.myuser = ...; }
else {};
# Works everywhere
home.packages = [ pkgs.my-package ];
}
# Handle sandbox and path issues
{
# Crostini has restricted paths
home.sessionPath = [ "$HOME/.local/bin" ];
# Use noSandbox for problematic builds
nixpkgs.config.allowUnfree = true;
nixpkgs.config.permittedInsecurePackages = [ "package-name" ];
}
Situation: Production deployment, flake check shows infinite recursion, 5 minutes to deploy Wrong: Comment out code, deploy broken version Right: Use isolation technique, find recursion point, fix dependency cycle
Situation: GitHub Actions failing, attribute not found, team waiting for fix Wrong: Add placeholder attribute, ship broken config Right: Use attribute triage, verify output structure, fix import path
Situation: Client demo in 10 minutes, nix eval shows type error Wrong: Cast to any, ignore type safety Right: Use typeOf debugging, identify mismatch, apply correct type conversion
Before declaring evaluation error fixed:
nix flake check passes without warningsjust check passes all validationsAll of these mean: Stop, apply systematic debugging, don't ship broken Nix.
Classifies errors into systematic categories and provides targeted guidance.
Runs the complete systematic debugging workflow with phases for triage, isolation, and resolution.
This main skill provides the high-level workflow, while the evaluation-errors sub-skill provides detailed examples and additional helper scripts for specific error patterns.