Error handling: Difference between revisions

From NixOS Wiki
imported>Pogobanane
Init a page about error handling and debugging
 
imported>Carschandler
m The previous example for config.warnings was returning a list inside of a list, which doesn't work properly
 
(9 intermediate revisions by one other user not shown)
Line 1: Line 1:
This page is a collection of facilities and tools from nix, nixpkgs and NixOS for error handling and debugging. You can use them to convey configuration errors to users or to debug nix expressions trough interactive or print debugging.   
This page is a collection of facilities and tools from nix, nixpkgs and NixOS for error handling and debugging. You can use them to convey configuration errors to users or to debug nix expressions trough interactive or print debugging.   


{{Expansion|This page is currently being worked on (04.05.2023) and desperately needs usage examples.}}
In most cases you will want to stick to the highest level abstraction: <code>config.warnings</code> or <code>lib.warn</code> and its relatives.  


In most cases you will want to stick to the highest level abstraction: config.warnings or lib.assertMsg
<syntaxHighlight lang=nix>
{ config, lib, ... }:  
# in any nix code:
lib.warn "This is a sample warning message."
{
    config.warnings = (
      # Some NixOS module: throw error, if services.foo.bar == true
      lib.optionals config.services.foo.bar "This is also a sample warning message, but invoked differently."
    );
}
</syntaxHighlight>


<give examples for both>
== Nix ==
 
The nix language has a construct to help with printing messages.


== Nix ==
* '''assert''': throw an error (see [https://nixos.org/manual/nix/stable/language/constructs.html?highlight=assert#assertions Nix manual: Assertions])


The nix language has some constructs to help with printing messages.
The nix language also comes with some related [https://nixos.org/manual/nix/stable/language/builtins.html builtin functions]:


- assert: throw an error [2]
* '''throw''': throw an error with a message
* '''abort''': same as throw, but always stop evaluation
* '''trace''': print to stderr
* '''traceVerbose''': print, but only when in <code>--trace-verbose</code> mode
* '''break''': breakpoint when in <code>--debugger</code> mode
* '''tryEval''': catch throws and asserts


The nix language already comes with some usefult builtin functions [3]:
Most of those functions (nix builtins as well as nixpkgs lib functions) take an expression <code>e</code> as their last argument which they return unmodified. Thus they are chained in front of some expression:


- throw: throw an error with a message
<syntaxHighlight lang=nix>
- abort: same as throw, but always stop evaluation
a = builtins.trace "trace message" {
- trace: print to stderr
  # what should be assigned to a
- traceVerbose: print, but only when in --trace-verbose mode
};
- break: breakpoint when in --debugger mode
</syntaxHighlight>
- tryEval: catch throws and asserts


Commonly, assert is combined with throw to generate meaningful error messages: assert condition || throw "message"; This pattern is essentially how lib.assertMsg works (see Sec. nixpkgs). [1]
Commonly, assert is combined with throw to generate meaningful error messages: <code>assert condition || throw "message";</code>. This pattern is essentially how <code>lib.assertMsg</code> works (see Sec. nixpkgs). <ref>[https://github.com/NixOS/nixpkgs/issues/154292 throw vs assert discussion]</ref>


== nixpkgs ==
== nixpkgs ==
Line 28: Line 44:
There are three main facilities for printing errors and do print debugging in nixpkgs:
There are three main facilities for printing errors and do print debugging in nixpkgs:


- lib.trivial.* [6]
* lib.trivial.* (see [https://nixos.org/manual/nixpkgs/stable/#sec-functions-library-trivial nixpkgs manual: lib.trivial])
    - lib.throwIf and throwIfNot
** lib.'''throwIf''' and throwIfNot
    - lib.warn, warnIf and warnIfNot
** lib.'''warn''', '''warnIf''' and warnIfNot
- lib.debug.*: tracing functions with some pretty printing [7, 9]
* [https://nixos.org/manual/nixpkgs/stable/#sec-functions-library-debug lib.'''debug'''.*]: tracing functions with some pretty printing (e.g. <code>lib.debug.traceIf</code>) <ref>[http://ryantm.github.io/nixpkgs/functions/library/debug/#sec-functions-library-debug Nixpkgs/docs: lib.debug]</ref>
- lib.asserts.*: assert functions [8]
* [https://nixos.org/manual/nixpkgs/stable/#sec-functions-library-asserts lib.'''asserts'''.*]: assert functions (e.g. <code>lib.asserts.assertMsg</code>)


These facilities also expose their attributes directly via lib.* (e.g. lib.throwIf).  
These facilities also expose their attributes directly via <code>lib.*</code> (e.g. <code>lib.throwIf</code>).  


Nixpkgs also has a debugging facility like nix's break: the breakpointHook (see [12]).
Nixpkgs also has a debugging facility like nix's <code>break</code>: the [https://nixos.org/manual/nixpkgs/stable/#breakpointhook breakpointHook].


== NixOS ==
== NixOS ==


The NixOS module system again wraps these library functions and makes them available via module options: [11, 10]
The NixOS module system again wraps these library functions and makes them available via module options (see [https://nixos.org/manual/nixos/stable/index.html#sec-assertions-warnings NixOS manual: Assertions/Warnings]): <ref>[https://github.com/NixOS/nixpkgs/blob/nixos-22.11/nixos/doc/manual/development/assertions.section.md Nixpkgs/docs: Assertions]</ref>
 
* <code>config.warnings = [];</code>
* <code>config.assertions = [];</code>
 
An example for a debugging facility in NixOS is running [https://nixos.org/manual/nixos/stable/index.html#sec-running-nixos-tests-interactively NixOS tests interactively].
 
== Debugging ==
 
<!-- put debugging into a heading for search engine optimisation -->
 
To summarise debugging approaches discussed in this article, you can use <code>break</code> to debug nix code, <code>breakpointHook</code> to debug nix builds and interactive tools to debug NixOS tests.


- config.warnings = [];
To find the location where variables get defined, you can use the following tools:
- config.assertions = [];


An example for a debugging facility in NixOS is running NixOS tests interactively (see [13]).
For bare nix code, use <code>builtins.unsafeGetAttrPos
</code> ([https://github.com/NixOS/nix/blob/b17c4290cf61d8a0386817b87231762c175097c5/tests/lang/eval-okay-getattrpos.nix example]) which returns the line and column of where an attribute is defined. It is undocumented and considered bad practice.


== See also ==
(soon to come [https://github.com/NixOS/nixpkgs/pull/249243 github PR]): For NixOS options unsafeGetAttrPos doesn't work, but the module system itself records that information: to find the location of <code>config.networking.hostName</code>, use <code>:p options.networking.hostName.declarationPositions</code>.


[1] throw vs assert discussion: https://github.com/NixOS/nixpkgs/issues/154292
== References ==
[2] https://nixos.org/manual/nix/stable/language/constructs.html?highlight=assert#assertions
[3] https://nixos.org/manual/nix/stable/language/builtins.html
[4] https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=warnings
[5] https://nixos.org/manual/nixpkgs/stable/
[6] https://nixos.org/manual/nixpkgs/stable/#sec-functions-library-trivial
[7] https://nixos.org/manual/nixpkgs/stable/#sec-functions-library-debug
[8] https://nixos.org/manual/nixpkgs/stable/#sec-functions-library-asserts
[9] http://ryantm.github.io/nixpkgs/functions/library/debug/#sec-functions-library-debug
[10] https://github.com/NixOS/nixpkgs/blob/nixos-22.11/nixos/doc/manual/development/assertions.section.md
[11] https://nixos.org/manual/nixos/stable/index.html#sec-assertions-warnings
[12] https://nixos.org/manual/nixpkgs/stable/#breakpointhook
[13] https://nixos.org/manual/nixos/stable/index.html#sec-running-nixos-tests-interactively

Latest revision as of 12:39, 19 January 2024

This page is a collection of facilities and tools from nix, nixpkgs and NixOS for error handling and debugging. You can use them to convey configuration errors to users or to debug nix expressions trough interactive or print debugging.

In most cases you will want to stick to the highest level abstraction: config.warnings or lib.warn and its relatives.

{ config, lib, ... }: 
# in any nix code:
lib.warn "This is a sample warning message."
{
    config.warnings = (
      # Some NixOS module: throw error, if services.foo.bar == true
      lib.optionals config.services.foo.bar "This is also a sample warning message, but invoked differently."
    );
}

Nix

The nix language has a construct to help with printing messages.

The nix language also comes with some related builtin functions:

  • throw: throw an error with a message
  • abort: same as throw, but always stop evaluation
  • trace: print to stderr
  • traceVerbose: print, but only when in --trace-verbose mode
  • break: breakpoint when in --debugger mode
  • tryEval: catch throws and asserts

Most of those functions (nix builtins as well as nixpkgs lib functions) take an expression e as their last argument which they return unmodified. Thus they are chained in front of some expression:

a = builtins.trace "trace message" {
   # what should be assigned to a
};

Commonly, assert is combined with throw to generate meaningful error messages: assert condition || throw "message";. This pattern is essentially how lib.assertMsg works (see Sec. nixpkgs). [1]

nixpkgs

There are three main facilities for printing errors and do print debugging in nixpkgs:

These facilities also expose their attributes directly via lib.* (e.g. lib.throwIf).

Nixpkgs also has a debugging facility like nix's break: the breakpointHook.

NixOS

The NixOS module system again wraps these library functions and makes them available via module options (see NixOS manual: Assertions/Warnings): [3]

  • config.warnings = [];
  • config.assertions = [];

An example for a debugging facility in NixOS is running NixOS tests interactively.

Debugging

To summarise debugging approaches discussed in this article, you can use break to debug nix code, breakpointHook to debug nix builds and interactive tools to debug NixOS tests.

To find the location where variables get defined, you can use the following tools:

For bare nix code, use builtins.unsafeGetAttrPos (example) which returns the line and column of where an attribute is defined. It is undocumented and considered bad practice.

(soon to come github PR): For NixOS options unsafeGetAttrPos doesn't work, but the module system itself records that information: to find the location of config.networking.hostName, use :p options.networking.hostName.declarationPositions.

References