Conversation
| If set to `true`, the Nix evaluator will replace evaluation errors | ||
| with a fixed value. |
There was a problem hiding this comment.
This should mention "in JSON output".
|
|
||
| std::vector<PrimOp> extraPrimOps; | ||
|
|
||
| Setting<bool> replaceEvalErrors{ |
There was a problem hiding this comment.
This should not be a setting but a flag to nix eval. We should avoid global settings that change what the output of commands looks like (e.g. nix eval --json), because then a user's settings might change the semantics of scripts that use those commands.
| bool isFileNotFoundError = dynamic_cast<FileNotFound *>(&e); | ||
| // Restrict replaceEvalErrors only only evaluation errors | ||
| if (state.settings.replaceEvalErrors && (isEvalError || isFileNotFoundError)) { | ||
| out.emplace(state.symbols[a->name], "«evaluation error»"); |
There was a problem hiding this comment.
This changes the semantics of builtins.toJSON and derivation with structured attrs (since it calls printValueAsJSON()), making them ignore exceptions which seems like a bad thing.
There was a problem hiding this comment.
This ties in with the comment about making it a flag instead of a setting.
In line with that, this could then refer to a new C++ parameter on printValueAsJSON, instead of state.settings.
| # cat ./out/subdir/bla | ||
| 123 | ||
|
|
||
| * Replace evaluation errors: |
There was a problem hiding this comment.
"Replace evaluation errors" is very vague (though the example does make it clear).
There was a problem hiding this comment.
It is clear when you have in mind the model of evaluation as term rewriting, although that's not how we introduce evaluation at all. In fact, we don't have a description of evaluation in the manual.
Reminded me of
although that doesn't go all that much into term rewriting either.
| When used with `--eval` and `--json`, replace any evaluation errors with the string | ||
| `"«evaluation error»"`. |
There was a problem hiding this comment.
This isn't really what it does. It should say something like: any attribute that throws an evaluation error is represented by a JSON property with the string value «evaluation error».
|
|
||
| ```console | ||
| $ nix-instantiate --eval --json --replace-eval-errors --expr '{ a = throw "fail"; }' | ||
| {"a":"«evaluation error»"} |
There was a problem hiding this comment.
Note that this representation of eval errors is ambiguous, since
nix-instantiate --eval --json --expr '{ a = "«evaluation error»"; }'
{"a":"«evaluation error»"}
produces the same output.
There was a problem hiding this comment.
While this is true, we already have such ambiguity, for instance:
nix-instantiate --eval --json --expr --strict '{ foo.outPath = "bar"; }'
{"foo":"bar"}
That said, something like {"__nix_evaluation_error": null} would be subjectively less ambiguous, and allows for a message or other data to be put where it says null.
Considering that this originated from a tryEval improvement, we don't have to be so afraid of the "chaos" in the message anymore!
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/comparing-module-system-configurations/59654/14 |
|
With this change (or a variant of it), I tried running it on a nixosConfiguration and the evaluator OOM :( |
I've been there. Gotta take care to avoid infinite recursion. |
|
I wonder if this something Since now we have a way to deterministically replace "errors" with a fixed string; we can set an upper-bound and fail when we breach it. Of course, this might not then solve the issue we sought to fix which was "a way to determine if someone sneaked in some nefarious NixOS options waiting to be enabled". |
|
Some infinite recursion are currently detectable by Nix. They're the kind that is trivial to detect (evaluating thunk x during evaluation of thunk x). Those are referred to as "black holes". But there's another kind, and that is the infinitely growing data structure. It is the latter kind that you are likely stumbled across. And I think there are not many of those. During Ocean Sprint 2025 the last of them may have been removed: So, I think we don't need to support those. |
ddbc00b to
7925634
Compare
Co-authored-by: Farid Zakaria <[email protected]>
7925634 to
88705f8
Compare
|
Interesting these have different outcomes when nix eval --expr 'let a = {} // { inherit a;}; in a'
{ a = «repeated»; }
nix eval --json --expr 'let a = {} // { inherit a;}; in a'
error: stack overflow (possible infinite recursion)We are building a demo file with some potential evaluation errors. {
missingAttr = let bar = { }; in bar.notExist;
insideAList = [ (throw "a throw") ];
deeper = { v = throw "v"; };
failedAssertion = assert true; assert false; null;
missingFile = builtins.readFile ./missing-file.txt;
missingImport = import ./missing-import.nix;
outOfBounds = builtins.elemAt [ 1 2 3 ] 100;
failedCoersion = "${1}";
failedAddition = 1.0 + "a string";
unicodeError = throw "» injecttion: error: v»";
repeatedRecursion = { a = let b = {inherit b;}; in b;};
}Fails (obviously) pretty bad when nix eval -f tests/functional/replace-eval-errors.nix
{
deeper = { v = «error: v»; };
failedAddition = «error: cannot add a string to a float»;
failedAssertion = «error: assertion 'false' failed»;
failedCoersion = «error: cannot coerce an integer to a string: 1»;
insideAList = [ «error: a throw» ];
missingAttr = «error: attribute 'notExist' missing»;
missingFile = «error: opening file '/home/fmzakari/code/github.com/NixOS/nix/tests/functional/missing-file.txt': No such file or directory»;
missingImport = «error: path '/home/fmzakari/code/github.com/NixOS/nix/tests/functional/missing-import.nix' does not exist»;
outOfBounds = «error: 'builtins.elemAt' called with index 100 on a list of size 3»;
repeatedRecursion = { a = { b = «repeated»; }; };
unicodeError = «error: » injecttion: error: v»»;
} |
|
Has there been any progress on this recently? I'm not sure what exactly needs to be solved before this can be upstreamed. Is there something I can help with? |
| )); | ||
| if (!fd) | ||
| throw SysError("opening file '%1%'", ap.string()); | ||
| throw SysError("opening file9 '%1%'", ap.string()); |
There was a problem hiding this comment.
Some cosmic radiation happened here ;)
This is a proper exception-based, C++ catchable stack overflow now: So that expression can be added to the test cases. |
|
I feel like this PR is pretty far along, but it has conflicts, and CI failed at the time. Logs have been removed unfortunately. |
Supersedes #12705
Co-authored-by: Farid Zakaria [email protected]
Motivation
Following up from #12705 but provides for the use case with a new CLI flag.
Context
Ditto.
Add 👍 to pull requests you find important.
The Nix maintainer team uses a GitHub project board to schedule and track reviews.