-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
Is your feature request related to a problem? Please describe.
Module system stack traces are too long. The low information density makes users believe that they won't learn anything from reading through it, but essential information is in the stack trace, thanks to builtins.addErrorContext.
Describe the solution you'd like
Instead of two stack trace modes, have three.
- default, no trace
--show-trace: summarized trace--full-trace: what's currently shown for--show-trace
Have a new primop hideTraceItems that takes a path and has the side effect of adding it to a set of paths that are treated specially.
When printing a stack trace, consult that set of paths. If the current trace item is from such a file, collapse the items into one, as follows.
- Gather up a subsequence of consecutive items from that file
- If the subsequence contains
addErrorContextitems, show those - If the subsequence does not contain such items, show a single trace item such as
while evaluating library code from <path> (use --full-trace to expand)
Instead of a file, we'll want to use two tiers of sets of files. One demarcates the start and end of the collapsed part, e.g. modules.nix + options.nix. The other set allows other files, such as attrsets.nix and lists.nix to occur within the collapsed part.
For the result, see the examples below.
Describe alternatives you've considered
-
Show
addErrorContexttraces only. I wish I'd thought of this earlier and I'll make a separate issue for this approach. -
Explicit entry / exit primops, so we avoid the file-based heuristics. However, entry and exit are not easily determined, as for example the module system has many possible entry and exit points. Consider for example user-defined types.
-
Represent the stack trace as first class Nix values, so that a special primop can install a handler. The handler function has type
[item] -> [item]and can not leak information to the main expressions. It's only capability is to summarize the trace. It's unclear how this would work when multiple versions of the module system are loaded, let alone multiple libraries that use this feature. -
Avoid the module system altogether
-
Wrap it in a yaml that's checked with json schema, killing modularity just for the purpose of improved error messages
Additional context
Example
A mutual recursion in NixOS:
nix-instantiate --expr 'with import ./. {}; nixos ({config,...}: { services.postgresql.enable = config.services.rabbitmq.enable; services.rabbitmq.enable = config.services.postgresql.enable; })' -A config.system.build.toplevel --show-trace
NB these traces are from before the item order was reversed.
--show-trace
error: infinite recursion encountered
… while evaluating definitions from `/home/user/h/nixpkgs/pkgs/top-level/all-packages.nix':
… while evaluating the option `services.postgresql.enable':
… while evaluating definitions from `/home/user/h/nixpkgs/pkgs/top-level/all-packages.nix':
… while evaluating the option `services.rabbitmq.enable':
… while evaluating definitions from `/home/user/h/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix':
… while evaluating the option `services.epmd.enable':
… while evaluating definitions from `/home/user/h/nixpkgs/nixos/modules/services/networking/epmd.nix':
… while evaluating the option `assertions':
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:265:72:
264| # For definitions that have an associated option
265| declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
| ^
266|
… from call site
at /home/user/h/nixpkgs/lib/attrsets.nix:533:20:
532| then recurse (path ++ [name]) value
533| else f (path ++ [name]) value;
| ^
534| in mapAttrs g;
… while evaluating 'g'
at /home/user/h/nixpkgs/lib/attrsets.nix:530:19:
529| g =
530| name: value:
| ^
531| if isAttrs value && cond value
… from call site
… while evaluating 'fold''
at /home/user/h/nixpkgs/lib/lists.nix:56:15:
55| len = length list;
56| fold' = n:
| ^
57| if n == len
… from call site
at /home/user/h/nixpkgs/lib/lists.nix:60:8:
59| else op (elemAt list n) (fold' (n + 1));
60| in fold' 0;
| ^
61|
… while evaluating 'foldr'
at /home/user/h/nixpkgs/lib/lists.nix:53:20:
52| */
53| foldr = op: nul: list:
| ^
54| let
… from call site
at /home/user/h/nixpkgs/nixos/modules/system/activation/top-level.nix:128:12:
127| # Replace runtime dependencies
128| system = foldr ({ oldDependency, newDependency }: drv:
| ^
129| pkgs.replaceDependency { inherit oldDependency newDependency drv; }
… while evaluating the attribute 'value'
… while evaluating definitions from `/home/user/h/nixpkgs/nixos/modules/system/activation/top-level.nix':
… while evaluating the option `system.build.toplevel':
… while evaluating 'g'
at /home/user/h/nixpkgs/lib/attrsets.nix:530:19:
529| g =
530| name: value:
| ^
531| if isAttrs value && cond value
… from call site
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/attrsets.nix:711:24:
710| let f = attrPath:
711| zipAttrsWith (n: values:
| ^
712| let here = attrPath ++ [n]; in
… from call site
--full-trace
error: infinite recursion encountered
at /home/user/h/nixpkgs/lib/modules.nix:723:9:
722| in warnDeprecation opt //
723| { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
| ^
724| inherit (res.defsFinal') highestPrio;
… while evaluating the attribute 'value'
at /home/user/h/nixpkgs/lib/modules.nix:723:9:
722| in warnDeprecation opt //
723| { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
| ^
724| inherit (res.defsFinal') highestPrio;
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:265:72:
264| # For definitions that have an associated option
265| declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
| ^
266|
… from call site
at /home/user/h/nixpkgs/lib/attrsets.nix:533:20:
532| then recurse (path ++ [name]) value
533| else f (path ++ [name]) value;
| ^
534| in mapAttrs g;
… while evaluating 'g'
at /home/user/h/nixpkgs/lib/attrsets.nix:530:19:
529| g =
530| name: value:
| ^
531| if isAttrs value && cond value
… from call site
… while evaluating the attribute 'value'
at /home/user/h/nixpkgs/lib/modules.nix:563:44:
562| defnsByName' = byName "config" (module: value:
563| [{ inherit (module) file; inherit value; }]
| ^
564| ) configs;
… while evaluating 'dischargeProperties'
at /home/user/h/nixpkgs/lib/modules.nix:810:25:
809| */
810| dischargeProperties = def:
| ^
811| if def._type or "" == "merge" then
… from call site
at /home/user/h/nixpkgs/lib/modules.nix:739:137:
738| defs' = concatMap (m:
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
| ^
740| ) defs;
… while evaluating definitions from `/home/user/h/nixpkgs/pkgs/top-level/all-packages.nix':
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:738:28:
737| # Process mkMerge and mkIf properties.
738| defs' = concatMap (m:
| ^
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
… from call site
at /home/user/h/nixpkgs/lib/modules.nix:738:17:
737| # Process mkMerge and mkIf properties.
738| defs' = concatMap (m:
| ^
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
… while evaluating the attribute 'values'
at /home/user/h/nixpkgs/lib/modules.nix:851:7:
850| in {
851| values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
| ^
852| inherit highestPrio;
… while evaluating the attribute 'values'
at /home/user/h/nixpkgs/lib/modules.nix:752:9:
751| in {
752| values = defs''';
| ^
753| inherit (defs'') highestPrio;
… while evaluating the attribute 'mergedValue'
at /home/user/h/nixpkgs/lib/modules.nix:758:5:
757| # Type-check the remaining definitions, and merge them. Or throw if no definitions.
758| mergedValue =
| ^
759| if isDefined then
… while evaluating the option `services.postgresql.enable':
… while evaluating the attribute 'value'
at /home/user/h/nixpkgs/lib/modules.nix:723:9:
722| in warnDeprecation opt //
723| { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
| ^
724| inherit (res.defsFinal') highestPrio;
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:265:72:
264| # For definitions that have an associated option
265| declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
| ^
266|
… from call site
at /home/user/h/nixpkgs/lib/attrsets.nix:533:20:
532| then recurse (path ++ [name]) value
533| else f (path ++ [name]) value;
| ^
534| in mapAttrs g;
… while evaluating 'g'
at /home/user/h/nixpkgs/lib/attrsets.nix:530:19:
529| g =
530| name: value:
| ^
531| if isAttrs value && cond value
… from call site
… while evaluating the attribute 'value'
at /home/user/h/nixpkgs/lib/modules.nix:563:44:
562| defnsByName' = byName "config" (module: value:
563| [{ inherit (module) file; inherit value; }]
| ^
564| ) configs;
… while evaluating 'dischargeProperties'
at /home/user/h/nixpkgs/lib/modules.nix:810:25:
809| */
810| dischargeProperties = def:
| ^
811| if def._type or "" == "merge" then
… from call site
at /home/user/h/nixpkgs/lib/modules.nix:739:137:
738| defs' = concatMap (m:
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
| ^
740| ) defs;
… while evaluating definitions from `/home/user/h/nixpkgs/pkgs/top-level/all-packages.nix':
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:738:28:
737| # Process mkMerge and mkIf properties.
738| defs' = concatMap (m:
| ^
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
… from call site
at /home/user/h/nixpkgs/lib/modules.nix:738:17:
737| # Process mkMerge and mkIf properties.
738| defs' = concatMap (m:
| ^
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
… while evaluating the attribute 'values'
at /home/user/h/nixpkgs/lib/modules.nix:851:7:
850| in {
851| values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
| ^
852| inherit highestPrio;
… while evaluating the attribute 'values'
at /home/user/h/nixpkgs/lib/modules.nix:752:9:
751| in {
752| values = defs''';
| ^
753| inherit (defs'') highestPrio;
… while evaluating the attribute 'mergedValue'
at /home/user/h/nixpkgs/lib/modules.nix:758:5:
757| # Type-check the remaining definitions, and merge them. Or throw if no definitions.
758| mergedValue =
| ^
759| if isDefined then
… while evaluating the option `services.rabbitmq.enable':
… while evaluating the attribute 'value'
at /home/user/h/nixpkgs/lib/modules.nix:723:9:
722| in warnDeprecation opt //
723| { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
| ^
724| inherit (res.defsFinal') highestPrio;
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:265:72:
264| # For definitions that have an associated option
265| declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
| ^
266|
… from call site
at /home/user/h/nixpkgs/lib/attrsets.nix:533:20:
532| then recurse (path ++ [name]) value
533| else f (path ++ [name]) value;
| ^
534| in mapAttrs g;
… while evaluating 'g'
at /home/user/h/nixpkgs/lib/attrsets.nix:530:19:
529| g =
530| name: value:
| ^
531| if isAttrs value && cond value
… from call site
… while evaluating the attribute 'condition'
at /home/user/h/nixpkgs/lib/modules.nix:881:14:
880| { _type = "if";
881| inherit condition content;
| ^
882| };
… while evaluating the attribute 'condition'
at /home/user/h/nixpkgs/lib/modules.nix:881:14:
880| { _type = "if";
881| inherit condition content;
| ^
882| };
… while evaluating the attribute 'condition'
at /home/user/h/nixpkgs/lib/modules.nix:881:14:
880| { _type = "if";
881| inherit condition content;
| ^
882| };
… while evaluating the attribute 'condition'
at /home/user/h/nixpkgs/lib/modules.nix:881:14:
880| { _type = "if";
881| inherit condition content;
| ^
882| };
… while evaluating 'dischargeProperties'
at /home/user/h/nixpkgs/lib/modules.nix:810:25:
809| */
810| dischargeProperties = def:
| ^
811| if def._type or "" == "merge" then
… from call site
at /home/user/h/nixpkgs/lib/modules.nix:739:137:
738| defs' = concatMap (m:
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
| ^
740| ) defs;
… while evaluating definitions from `/home/user/h/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix':
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:738:28:
737| # Process mkMerge and mkIf properties.
738| defs' = concatMap (m:
| ^
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
… from call site
at /home/user/h/nixpkgs/lib/modules.nix:738:17:
737| # Process mkMerge and mkIf properties.
738| defs' = concatMap (m:
| ^
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
… while evaluating the attribute 'values'
at /home/user/h/nixpkgs/lib/modules.nix:851:7:
850| in {
851| values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
| ^
852| inherit highestPrio;
… while evaluating the attribute 'values'
at /home/user/h/nixpkgs/lib/modules.nix:752:9:
751| in {
752| values = defs''';
| ^
753| inherit (defs'') highestPrio;
… while evaluating the attribute 'mergedValue'
at /home/user/h/nixpkgs/lib/modules.nix:758:5:
757| # Type-check the remaining definitions, and merge them. Or throw if no definitions.
758| mergedValue =
| ^
759| if isDefined then
… while evaluating the option `services.epmd.enable':
… while evaluating the attribute 'value'
at /home/user/h/nixpkgs/lib/modules.nix:723:9:
722| in warnDeprecation opt //
723| { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
| ^
724| inherit (res.defsFinal') highestPrio;
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:265:72:
264| # For definitions that have an associated option
265| declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
| ^
266|
… from call site
at /home/user/h/nixpkgs/lib/attrsets.nix:533:20:
532| then recurse (path ++ [name]) value
533| else f (path ++ [name]) value;
| ^
534| in mapAttrs g;
… while evaluating 'g'
at /home/user/h/nixpkgs/lib/attrsets.nix:530:19:
529| g =
530| name: value:
| ^
531| if isAttrs value && cond value
… from call site
… while evaluating the attribute 'condition'
at /home/user/h/nixpkgs/lib/modules.nix:881:14:
880| { _type = "if";
881| inherit condition content;
| ^
882| };
… while evaluating the attribute 'condition'
at /home/user/h/nixpkgs/lib/modules.nix:881:14:
880| { _type = "if";
881| inherit condition content;
| ^
882| };
… while evaluating 'dischargeProperties'
at /home/user/h/nixpkgs/lib/modules.nix:810:25:
809| */
810| dischargeProperties = def:
| ^
811| if def._type or "" == "merge" then
… from call site
at /home/user/h/nixpkgs/lib/modules.nix:739:137:
738| defs' = concatMap (m:
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
| ^
740| ) defs;
… while evaluating definitions from `/home/user/h/nixpkgs/nixos/modules/services/networking/epmd.nix':
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:738:28:
737| # Process mkMerge and mkIf properties.
738| defs' = concatMap (m:
| ^
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
… from call site
at /home/user/h/nixpkgs/lib/modules.nix:738:17:
737| # Process mkMerge and mkIf properties.
738| defs' = concatMap (m:
| ^
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
… while evaluating the attribute 'values'
at /home/user/h/nixpkgs/lib/modules.nix:851:7:
850| in {
851| values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
| ^
852| inherit highestPrio;
… while evaluating the attribute 'values'
at /home/user/h/nixpkgs/lib/modules.nix:752:9:
751| in {
752| values = defs''';
| ^
753| inherit (defs'') highestPrio;
… while evaluating the attribute 'mergedValue'
at /home/user/h/nixpkgs/lib/modules.nix:758:5:
757| # Type-check the remaining definitions, and merge them. Or throw if no definitions.
758| mergedValue =
| ^
759| if isDefined then
… while evaluating the option `assertions':
… while evaluating the attribute 'value'
at /home/user/h/nixpkgs/lib/modules.nix:723:9:
722| in warnDeprecation opt //
723| { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
| ^
724| inherit (res.defsFinal') highestPrio;
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:265:72:
264| # For definitions that have an associated option
265| declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
| ^
266|
… from call site
at /home/user/h/nixpkgs/lib/attrsets.nix:533:20:
532| then recurse (path ++ [name]) value
533| else f (path ++ [name]) value;
| ^
534| in mapAttrs g;
… while evaluating 'g'
at /home/user/h/nixpkgs/lib/attrsets.nix:530:19:
529| g =
530| name: value:
| ^
531| if isAttrs value && cond value
… from call site
… while evaluating 'fold''
at /home/user/h/nixpkgs/lib/lists.nix:56:15:
55| len = length list;
56| fold' = n:
| ^
57| if n == len
… from call site
at /home/user/h/nixpkgs/lib/lists.nix:60:8:
59| else op (elemAt list n) (fold' (n + 1));
60| in fold' 0;
| ^
61|
… while evaluating 'foldr'
at /home/user/h/nixpkgs/lib/lists.nix:53:20:
52| */
53| foldr = op: nul: list:
| ^
54| let
… from call site
at /home/user/h/nixpkgs/nixos/modules/system/activation/top-level.nix:128:12:
127| # Replace runtime dependencies
128| system = foldr ({ oldDependency, newDependency }: drv:
| ^
129| pkgs.replaceDependency { inherit oldDependency newDependency drv; }
… while evaluating the attribute 'value'
at /home/user/h/nixpkgs/lib/modules.nix:563:44:
562| defnsByName' = byName "config" (module: value:
563| [{ inherit (module) file; inherit value; }]
| ^
564| ) configs;
… while evaluating 'dischargeProperties'
at /home/user/h/nixpkgs/lib/modules.nix:810:25:
809| */
810| dischargeProperties = def:
| ^
811| if def._type or "" == "merge" then
… from call site
at /home/user/h/nixpkgs/lib/modules.nix:739:137:
738| defs' = concatMap (m:
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
| ^
740| ) defs;
… while evaluating definitions from `/home/user/h/nixpkgs/nixos/modules/system/activation/top-level.nix':
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:738:28:
737| # Process mkMerge and mkIf properties.
738| defs' = concatMap (m:
| ^
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
… from call site
at /home/user/h/nixpkgs/lib/modules.nix:738:17:
737| # Process mkMerge and mkIf properties.
738| defs' = concatMap (m:
| ^
739| map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
… while evaluating the attribute 'values'
at /home/user/h/nixpkgs/lib/modules.nix:851:7:
850| in {
851| values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
| ^
852| inherit highestPrio;
… while evaluating the attribute 'values'
at /home/user/h/nixpkgs/lib/modules.nix:752:9:
751| in {
752| values = defs''';
| ^
753| inherit (defs'') highestPrio;
… while evaluating the attribute 'mergedValue'
at /home/user/h/nixpkgs/lib/modules.nix:758:5:
757| # Type-check the remaining definitions, and merge them. Or throw if no definitions.
758| mergedValue =
| ^
759| if isDefined then
… while evaluating the option `system.build.toplevel':
… while evaluating the attribute 'value'
at /home/user/h/nixpkgs/lib/modules.nix:723:9:
722| in warnDeprecation opt //
723| { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
| ^
724| inherit (res.defsFinal') highestPrio;
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/modules.nix:265:72:
264| # For definitions that have an associated option
265| declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
| ^
266|
… from call site
at /home/user/h/nixpkgs/lib/attrsets.nix:533:20:
532| then recurse (path ++ [name]) value
533| else f (path ++ [name]) value;
| ^
534| in mapAttrs g;
… while evaluating 'g'
at /home/user/h/nixpkgs/lib/attrsets.nix:530:19:
529| g =
530| name: value:
| ^
531| if isAttrs value && cond value
… from call site
… while evaluating anonymous lambda
at /home/user/h/nixpkgs/lib/attrsets.nix:711:24:
710| let f = attrPath:
711| zipAttrsWith (n: values:
| ^
712| let here = attrPath ++ [n]; in
… from call site
Priorities
Add 👍 to issues you find important.