Wrote a small sweep over an overlay that wraps each package in builtins.tryEval and lists the ones that fail. It reported everything as fine, including one I had broken on purpose with a throw in its dependency list.
The manual is clear about it once you read the whole entry. tryEval evaluates shallowly. A package is an attribute set, so reaching that form is enough to succeed, and nothing inside it gets forced.
let
pkg = { name = "demo"; deps = throw "broken on purpose"; };
in {
shallow = (builtins.tryEval pkg).success;
deep = (builtins.tryEval (builtins.deepSeq pkg pkg)).success;
}
With --strict that gives shallow = true and deep = false. The rest of the entry matters as well. It only catches throw and assert, so an abort or a type error from a builtin stops the whole sweep instead of marking one package. The sweep as written answered a narrower question than its name suggests, and I'm not yet sure what the right thing to force is for real packages.