Skip to content

fix(module): explicitly set defaultText to literal#202

Merged
water-sucks merged 1 commit intonix-community:mainfrom
ilkecan:fix-nixos-module-evaluation
Mar 4, 2026
Merged

fix(module): explicitly set defaultText to literal#202
water-sucks merged 1 commit intonix-community:mainfrom
ilkecan:fix-nixos-module-evaluation

Conversation

@ilkecan
Copy link
Contributor

@ilkecan ilkecan commented Mar 4, 2026

... as evaluating the default value depends on config.

https://nixos.org/manual/nixpkgs/stable/#function-library-lib.options.literalExpression


Without this using the nixos module was not possible:

> Building NixOS configuration
evaluation warning: The option `programs.nixos-cli.config' defined in `/nix/store/p36y2vxzspcj1lwpyls8nzs8ydbdsrx3-source/hosts/mephistopheles/nix/nixos-cli.nix' has been renamed to `programs.nixos-cli.settings'.
error:
       … while calling the 'head' builtin
         at /nix/store/vc87yvzkz00pppybcmsqvmwa1i3zi2ar-nixpkgs/lib/attrsets.nix:1696:13:
         1695|           if length values == 1 || pred here (elemAt values 1) (head values) then
         1696|             head values
             |             ^
         1697|           else

       … while evaluating the attribute 'value'
         at /nix/store/vc87yvzkz00pppybcmsqvmwa1i3zi2ar-nixpkgs/lib/modules.nix:1118:7:
         1117|     // {
         1118|       value = addErrorContext "while evaluating the option `${showOption loc}':" value;
             |       ^
         1119|       inherit (res.defsFinal') highestPrio;

       … while evaluating the option `system.build.toplevel':

       … while evaluating definitions from `/nix/store/vc87yvzkz00pppybcmsqvmwa1i3zi2ar-nixpkgs/nixos/modules/system/activation/top-level.nix':

       … while evaluating the option `system.systemBuilderArgs':

       … while evaluating definitions from `/nix/store/vc87yvzkz00pppybcmsqvmwa1i3zi2ar-nixpkgs/nixos/modules/system/activation/activatable-system.nix':

       … while evaluating the option `system.activationScripts.etc.text':

       … while evaluating definitions from `/nix/store/vc87yvzkz00pppybcmsqvmwa1i3zi2ar-nixpkgs/nixos/modules/system/etc/etc-activation.nix':

       … while evaluating definitions from `/nix/store/vc87yvzkz00pppybcmsqvmwa1i3zi2ar-nixpkgs/nixos/modules/system/etc/etc.nix':

       … while evaluating the option `environment.etc.dbus-1.source':

       … while evaluating the default value of option `programs.nixos-cli.option-cache.enable`

       (stack trace truncated; use '--show-trace' to show the full, detailed trace)

       error: attribute 'documentation' missing
       at /nix/store/w8833rjnwyyly1506kkdb75g83mxd18h-source/nix/module.nix:117:19:
          116|         type = types.bool;
          117|         default = config.documentation.nixos.enable;
             |                   ^
          118|         description = "Prebuild JSON cache for `nixos option` command";
┏━ 1 Errors:
 ⋮
┃
┃        … while evaluating definitions from `/nix/store/vc87yvzkz00pppybcmsqvmwa1i3zi2ar-nixpkgs/nixos/modules/syst…
┃
┃        … while evaluating definitions from `/nix/store/vc87yvzkz00pppybcmsqvmwa1i3zi2ar-nixpkgs/nixos/modules/syst…
┃
┃        … while evaluating the option `environment.etc.dbus-1.source':
┃
┃        … while evaluating the default value of option `programs.nixos-cli.option-cache.enable`
┃
┃        (stack trace truncated; use '--show-trace' to show the full, detailed trace)
┃
┃        error: attribute 'documentation' missing
┃        at /nix/store/w8833rjnwyyly1506kkdb75g83mxd18h-source/nix/module.nix:117:19:
┃           116|         type = types.bool;
┃           117|         default = config.documentation.nixos.enable;
┃              |                   ^
┃           118|         description = "Prebuild JSON cache for `nixos option` command";
┣━━━
┗━ ∑ ⚠ Exited with 1 errors reported by nix at 17:27:18 after 12s
Error:
   0: Failed to build configuration
   1: Command exited with status Exited(1)

Location:
   src/commands.rs:880

... as evaluating the default value depends on `config`.
Copy link
Collaborator

@water-sucks water-sucks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but I have not been able to reproduce this issue. Can you please file an issue report with more information on the eval failure along with a minimal reproducible example? I don't know why config.documentation would be missing.

@water-sucks water-sucks merged commit 6a4aed7 into nix-community:main Mar 4, 2026
2 checks passed
@ilkecan
Copy link
Contributor Author

ilkecan commented Mar 4, 2026

I have encountered the problem after simply adding inputs.nixos-cli.nixosModules.nixos-cli to the modules and adding the example configuration from the document:

  programs.nixos-cli = {
    enable = true;
    config = {
      # Whatever settings desired.
    }
  };

but I will try to create a MRE to ensure this was not caused by any of my existing configuration.

@ilkecan ilkecan deleted the fix-nixos-module-evaluation branch March 4, 2026 20:17
@water-sucks
Copy link
Collaborator

All good, I want to make sure that nixos-cli works regardless of what people have in their configurations. I wouldn't rely on config.documentation existing if there was potential for it to not exist in the config, and would set a reasonable fallback to prevent eval failures.

@ilkecan
Copy link
Contributor Author

ilkecan commented Mar 13, 2026

Hello @water-sucks, I was able to create a MRE with the help of Claude:

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
  outputs = { nixpkgs, ... }: {
    nixosConfigurations.test = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        {
          # this is important
          documentation.nixos.includeAllModules = true;

          # boilerplate
          boot.loader.grub.device = "/dev/sda";
          fileSystems."/" = { device = "/dev/sda1"; fsType = "ext4"; };
          system.stateVersion = "25.11";
        }
        ({ config, lib, ... }: {
          options.test.foo = lib.mkOption {
            type = lib.types.bool;
            default = config.documentation.nixos.enable;
            description = "triggers the bug";
          };
        })
      ];
    };
  };
}

Running nix build .#nixosConfigurations.test.config.system.build.toplevel on the flake above results in the same error

error
error:
       … while calling the 'head' builtin
         at «github:NixOS/nixpkgs/3e20095fe3c6cbb1ddcef89b26969a69a1570776?narHash=sha256-SEzUWw2Rf5Ki3bcM26nSKgbeoqi2uYy8IHVBqOKjX3w%3D»/lib/attrsets.nix:1696:13:
         1695|           if length values == 1 || pred here (elemAt values 1) (head values) then
         1696|             head values
             |             ^
         1697|           else

       … while evaluating the attribute 'value'
         at «github:NixOS/nixpkgs/3e20095fe3c6cbb1ddcef89b26969a69a1570776?narHash=sha256-SEzUWw2Rf5Ki3bcM26nSKgbeoqi2uYy8IHVBqOKjX3w%3D»/lib/modules.nix:1118:7:
         1117|     // {
         1118|       value = addErrorContext "while evaluating the option `${showOption loc}':" value;
             |       ^
         1119|       inherit (res.defsFinal') highestPrio;

       … while evaluating the option `system.build.toplevel':

       … while evaluating definitions from `/nix/store/cy6cvjfw0729jx30b2khdbjak8wbbcs1-source/nixos/modules/system/activation/top-level.nix':

       … while evaluating the option `system.systemBuilderArgs':

       … while evaluating definitions from `/nix/store/cy6cvjfw0729jx30b2khdbjak8wbbcs1-source/nixos/modules/system/activation/activatable-system.nix':

       … while evaluating the option `system.activationScripts.etc.text':

       … while evaluating definitions from `/nix/store/cy6cvjfw0729jx30b2khdbjak8wbbcs1-source/nixos/modules/system/etc/etc-activation.nix':

       … while evaluating definitions from `/nix/store/cy6cvjfw0729jx30b2khdbjak8wbbcs1-source/nixos/modules/system/etc/etc.nix':

       … while evaluating the option `environment.etc.dbus-1.source':

       … while evaluating the default value of option `test.foo`

       (stack trace truncated; use '--show-trace' to show the full, detailed trace)

       error: attribute 'documentation' missing
       at /nix/store/vksm98ymjb15ly63kxk3hwgj47pdqs3v-source/flake.nix:19:23:
           18|             type = lib.types.bool;
           19|             default = config.documentation.nixos.enable;
             |                       ^
           20|             description = "triggers the bug";

And here's the explanation Claude gave me:

The split build in nixpkgs documentation module

The NixOS documentation module (documentation.nix) splits option doc generation into two partitions:

  1. Lazy (cacheable) — base NixOS modules whose docs can be built in a sandbox (separate nix-instantiate). Includes most standard modules like the documentation module itself.
  2. Eager (non-cacheable) — modules that can't be sandboxed. When includeAllModules = true, all user modules (including nixos-cli) are added here.

The eager partition is evaluated via scrubbedEval — a separate evalModules call (documentation.nix:75-90). Critically, it only includes docModules.eager, which does not include the
documentation module (because it passes canCacheDocs and lands in the lazy partition).

The failure chain

  1. scrubbedEval loads nixos-cli but not the documentation module
  2. scrubbedEval.options is passed to nixosOptionsDoc → make-options-doc/default.nix
  3. make-options-doc calls lib.optionAttrSetToDocList on those options (line 114)
  4. For each option, it evaluates renderOptionValue (opt.defaultText or opt.default) (line 591 in lib/options.nix)
  5. For programs.nixos-cli.option-cache.enable: no defaultText, so it evaluates opt.default = config.documentation.nixos.enable
  6. But config here is the scrubbedEval's config, where the documentation module is absent
  7. Error: attribute 'documentation' missing

This then gets serialized via builtins.toJSON optionsNix (make-options-doc:218) into the optionsJSON derivation, which is referenced by manual.nixos-configuration-reference-manpage, which
is added to environment.systemPackages by the documentation module.

Why defaultText fixes it

Line 591 in lib/options.nix:
renderOptionValue (opt.defaultText or opt.default)

With defaultText set, opt.defaultText is used instead — it's a literalExpression (a static string), so config.documentation.nixos.enable is never evaluated in the scrubbedEval context.

Why you couldn't reproduce with a trivial config

Without documentation.nixos.includeAllModules = true (the default is false), user modules aren't added to the scrubbedEval, so the nixos-cli option defaults are never evaluated in that
context.

So it seems using config.* as a default value was not the actual problem; it was that:

  • manual generation run a seperate evalModules, which doesn't include the built-in modules
  • our default value used a path (option) that was defined by one of those missing built-in modules

But in any case, whenever default references config or pkgs, adding a defaultText = lib.literalExpression "..." seems like the better practice to me; because default might be evaluated in contexts where those arguments aren't fully available.

@water-sucks
Copy link
Collaborator

Whoa! That's quite deep into the builtin documentation generation system you had to dive into, super thankful for the time that you took on this.

This actually does have implications for the way nixos option computes the option list, since any option (even out-of-tree) that relies on a configuration option default with these built-in modules can fail in that context.

Unfortunately, there isn't much I can do about upstream modules failing to eval in the option list generator like this, but I recently added a way for users to exclude options from this list in #183, which is probably the most I can do to prevent eval failures for users.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants