2026-06-06 Untangling the update -c Failure Cascade
What I set out to do
Fix one error: update -c died on nix flake update with error: creating pipe: Too many open files. Expected a quick ulimit bump.
What I actually did
It turned into a five-layer cascade, each layer only visible once the one before it was cleared:
- File-descriptor limit (the reported bug). macOS launchd hands processes a 256 open-file soft limit (
launchctl limit maxfiles), andnix flake updatefans parallel fetches/pipes across every transitive input until it exhausts that. The trap: every shell I could spawn reportedulimit -n= 1048576, because Claude Code runs on Node and Node auto-raisesRLIMIT_NOFILEat startup, masking the real terminal limit. Had to reason about it vialaunchctl, notulimit. Fix:updateself-raises to 65536, and~/.config/zsh/.zshrcraises it for all interactive shells. - litellm patch broke. The nixpkgs bump pulled litellm 1.83.14 → 1.86.0, and my
litellm-25240-responses-custom-llm-providerpatch lost 2 of 6 hunks. Confirmed it was a realpatchfailure (not a Bash truncation/timeout) by rebuilding the derivation in isolation. Regenerated the patch against the 1.86.0 source by diffing a real copy; the build’s own 9-marker assertion (3 pristine + 6 patched) passed. - settings.json collision. HM refused to clobber
~/.claude/settings.json(now a plain file, not the symlink). Diffed it against the generated output: every difference was a/nix/storehash drift, no real edits, so safe to reclaim. - prek vs git-hooks.nix. The commit step failed with prek “migration mode”. Root cause: the devshell
shellHookranconfig.pre-commit.installationScript, installing git-hooks.nix’s classic shim over prek’s on every entry. Per Nix - Home Manager and ADR 0005, git-hooks.nix only generates the config and prek owns execution, so the shellHook now doesprek install -f. Proved idempotent by planting a foreign shim and re-entering. - Commit hygiene. The script’s bare
git commithad swept my fix files into the lock-bump commit. Split history into atomic commits, then fixed the script itself to pathspec-scope its commits (git commit -- <lock>) so it can’t recur.
What was striking
- The masking effect of Node raising the fd limit was the trickiest part. The diagnostic tool’s environment lied about the symptom. Lesson reinforced: inspect the live system at the right layer (
launchctl), don’t trust the convenient readout. - “Is it failing or just timing out?” was a good challenge. Reproducing the litellm build in isolation was the right way to answer it with evidence rather than assertion.
- One reported error, five real bugs. Flake updates surface latent coupling (version-pinned patches, plain-file drift, hook-installer conflicts) all at once.
Related
- Nix - Home Manager
- 2026-05-14 Upstream Issue Cleanup and Update Script (the litellm upstream-patch stack this rebase touched)