Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flatpak can't access system fonts and icons #119433

Closed
ilya-fedin opened this issue Apr 14, 2021 · 38 comments · Fixed by #262462
Closed

Flatpak can't access system fonts and icons #119433

ilya-fedin opened this issue Apr 14, 2021 · 38 comments · Fixed by #262462
Labels
0.kind: bug Something is broken

Comments

@ilya-fedin
Copy link
Contributor

Describe the bug
Flatpak mounts /usr/share/icons and /usr/share/fonts to /run/host/share/icons and /run/host/fonts respectively. However, this doesn't work on NixOS since all fonts/icons are in their own directories in /nix/store. While the lack of icon theme in flatpak apps is not so a big problem, the lack of system fonts leads to inability to read text written in some languages. The font for Latin characters is Deja Vu is that case that is too bold and has bad readability (at least for me). I'm suffering from this issue for a long time and thought that at some point someone raise this or this will be fixed at some time by someone, but I can't take it anymore :(

To Reproduce
Steps to reproduce the behavior:

  1. Install any app from flatpak
  2. Realize that it uses Deja Vu font and can't display various language families (e.g. CJK), can't display graphical symbols, etc.
  3. Realize that it can't use system icons even if it tries

Expected behavior
Flatpak is patched so that it can read fonts and icons. Or some option that enables centralized storage of fonts/icons and Flatpak uses it.

Screenshots
изображение
изображение

How it looks like outside of flatpak:
изображение

Notify maintainers
@jtojnar

Metadata

 - system: `"x86_64-linux"`
 - host os: `Linux 5.11.11-zen1, NixOS, 21.05pre-git (Okapi)`
 - multi-user?: `yes`
 - sandbox: `yes`
 - version: `nix-env (Nix) 2.3.10`
 - nixpkgs: `/run/nixpkgs`

Maintainer information:

# a list of nixpkgs attributes affected by the problem
attribute: flatpak
# a list of nixos modules affected by the problem
module: services.flatpak
@ilya-fedin ilya-fedin added the 0.kind: bug Something is broken label Apr 14, 2021
@ilya-fedin
Copy link
Contributor Author

The directory can be overridden using a configure flag:

This probably won't help without some option to copy all the fonts to one directory...

There's fonts.fontDir.enable, but it links fonts, so flatpak apps can't access them :(

@solson
Copy link
Member

solson commented May 12, 2021

I recently switched from the nixpkgs spotify to spotify in flatpak and I didn't notice this issue until today, but it's a pretty severe issue. Here's an example of a Japanese artist's page when there is no access to system fonts for CJK fallback:

image

@solson
Copy link
Member

solson commented May 12, 2021

My poor, temporary solution was to copy NotoSansCJK.ttc (a 118 MiB file...) to ~/.local/share/fonts, which flatpak apps get automatic access to. Using a symlink doesn't work, since it links outside of what flatpak is allowed access to.

A proper solution might involve collecting a list of all the store paths for system fonts, and building a big list of bubblewrap --ro-binds that gets passed to flatpak, which could be done in the flatpak NixOS module. For example:

--ro-bind /nix/store/lz4p3vhvxzjwycbdaj9xfav41h4c1j7v-noto-fonts-cjk-2.001/share/fonts/opentype/noto-cjk/NotoSansCJK.ttc /run/host/fonts/NotoSansCJK.ttc

@ilya-fedin
Copy link
Contributor Author

I hope that will be solved once, since I don't really want to copy all installed fonts to home :(

@ilya-fedin
Copy link
Contributor Author

Looks like I found a solution for myself that won't double occupied space... I copied the fontconfig module and modified it in a way so that it copies contents of all the font packages to one package and doesn't reference these original packages, so they're removed right after nix-collect-gabage. I also created a bind mount to avoid flatpak rebuild for myself.
ilya-fedin/nur-repository@e5bba93

I think I will do something like this for icons as well.

@ilya-fedin
Copy link
Contributor Author

I think I will do something like this for icons as well.

So it is: ilya-fedin/nur-repository@2aa0966

Unlike the font one, it duplicates the icons, but I have only 300 MB of them (unlike fonts), so that's acceptable for me.

@zendo
Copy link
Contributor

zendo commented Dec 5, 2021

My solution:
fonts.fontDir.enable = true;
ln -s /run/current-system/sw/share/X11/fonts ~/.local/share/fonts
flatpak install flatseal
then enable the All system files option for your application in flatseal.

@HadetTheUndying
Copy link

My solution: fonts.fontDir.enable = true; ln -s /run/current-system/sw/share/X11/fonts ~/.local/share/fonts flatpak install flatseal then enable the All system files option for your application in flatseal.

That's not really a great solution since it pretty much eliminates the security benefits of running proprietary apps inside flatpak containers

@Lucy-Nya
Copy link

My solution: fonts.fontDir.enable = true; ln -s /run/current-system/sw/share/X11/fonts ~/.local/share/fonts flatpak install flatseal then enable the All system files option for your application in flatseal.

I highly advise against doing that since that would defeat the purpose of using flatpak,
A safer way of dealing with this issue is to download (or copy) the fonts you want your apps to use into either ~/.fonts or ~/.local/share/fonts and giving flatpak apps access to the location u choose via flatseal

the same can be done with the cursor, again just download the theme u use, place it into ~/.icons or ~/.local/share/icons and giving the apps access to that

in both cases you can either to that on a per-app basis or just add access to those folders in the global section

@jtojnar
Copy link
Member

jtojnar commented Jun 10, 2022

If you have home manager, you could try something like the following (untested):

home.file.".local/share/flatpak/overrides/global".text = ''
  [Context]
  filesystems=/run/current-system/sw/share/X11/fonts:ro;/nix/store:ro
'';

Or just add those in Flatseal. Or edit the ~/.local/share/flatpak/overrides/global file manually. That should be slightly safer.

@OfficialBoyfriend
Copy link

OfficialBoyfriend commented Sep 7, 2022

Using cp -R -L /run/current-system/sw/share/X11/fonts ~/.local/share/fonts to copy all fonts to the user's home directory can temporarily solve the font problem. Of course, this is not a long-term solution.

@qcloutier
Copy link

qcloutier commented Sep 22, 2022

If you have home manager, you could try something like the following (untested):

home.file.".local/share/flatpak/overrides/global".text = ''
  [Context]
  filesystems=/run/current-system/sw/share/X11/fonts:ro;/nix/store:ro
'';

Or just add those in Flatseal. Or edit the ~/.local/share/flatpak/overrides/global file manually. That should be slightly safer.

Doing this causes a lot of applications to break. Some apps such as Pika Backup and Clapper won't launch at all. For example, NewsFlash complains about the following:

/usr/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by /nix/store/zdv5zz22wnanyz634h9x8g7mh5hk82m3-libproxy-0.4.17/lib/libproxy.so.1)
Failed to load module: /nix/store/b8fczjp513jvmp1y0y24rrl5ak5yz7sb-glib-networking-2.72.2/lib/gio/modules/libgiolibproxy.so

It seems the only reliable solution is copying the fonts / icons to my $HOME. Which really sucks.

@qcloutier
Copy link

In my case, icons are taking up way more space than fonts, so those modules don't really help much unfortunately.

@alaviss
Copy link
Contributor

alaviss commented Nov 25, 2022

Just in case it can be of use to anyone, I made a workaround for this issue using bindfs:

{ config, pkgs, ... }:

{
  system.fsPackages = [ pkgs.bindfs ];
  fileSystems = let
    mkRoSymBind = path: {
      device = path;
      fsType = "fuse.bindfs";
      options = [ "ro" "resolve-symlinks" "x-gvfs-hide" ];
    };
    aggregatedFonts = pkgs.buildEnv {
      name = "system-fonts";
      paths = config.fonts.fonts;
      pathsToLink = [ "/share/fonts" ];
    };
  in {
    # Create an FHS mount to support flatpak host icons/fonts
    "/usr/share/icons" = mkRoSymBind (config.system.path + "/share/icons");
    "/usr/share/fonts" = mkRoSymBind (aggregatedFonts + "/share/fonts");
  };
}

The main trick here is that bindfs resolves all symlink, allowing them to be accessed without letting /nix into the sandbox.

@yswtrue
Copy link

yswtrue commented Feb 7, 2023

Just in case it can be of use to anyone, I made a workaround for this issue using bindfs:

{ config, pkgs, ... }:

{
  system.fsPackages = [ pkgs.bindfs ];
  fileSystems = let
    mkRoSymBind = path: {
      device = path;
      fsType = "fuse.bindfs";
      options = [ "ro" "resolve-symlinks" "x-gvfs-hide" ];
    };
    aggregatedFonts = pkgs.buildEnv {
      name = "system-fonts";
      paths = config.fonts.fonts;
      pathsToLink = [ "/share/fonts" ];
    };
  in {
    # Create an FHS mount to support flatpak host icons/fonts
    "/usr/share/icons" = mkRoSymBind (config.system.path + "/share/icons");
    "/usr/share/fonts" = mkRoSymBind (aggregatedFonts + "/share/fonts");
  };
}

The main trick here is that bindfs resolves all symlink, allowing them to be accessed without letting /nix into the sandbox.

This works for me. Thank you very much!!!

@thibaultmol
Copy link
Contributor

Hope someone fixes this in nixos itself so we don't need the workarounds

@jasongodev
Copy link

One possible solution to this issue is to remove x11 fonts.

This issue occurs mostly in Debian and openSUSE based distros with x11 fonts installed. Fedora and RHEL-based distros are usually not affected by default. Other distros may be affected if they have x11 fonts installed.

The issue happens due to the following:

  1. When flatpak apps loads, it will prioritize the system fonts installed in the host.
  2. If the font is not found in the system, it will look at the user font configuration and font files.
  3. If the font is still not found in the user space, the aliases for fonts will kick in. As long as there is an equivalent family of the target font, it will use that font.

For example, in a Wikipedia webpage, the title of the topic uses 'Linux Libertine', 'Georgia', 'Times', serif; in that order. In default Debian and openSUSE installation Linux Libertine and Georgia are not installed. The next candidate is Times which is provided by an old x11 bitmap font. In Fedora, Times as x11 font is not present, therefore it fallbacks to serif which the flatpak runtime environment provides as TeX Gyre Termes.

What we need is to remove x11 fonts so we can have that fallback mechanism just like with other distros.

In Debian remove x11 fonts using:

sudo apt remove xfonts-base xfonts-100dpi xfonts-75dpi

In openSUSE based distros, use this code:

sudo zypper remove xorg-x11-fonts xorg-x11-fonts-legacy

@ilya-fedin
Copy link
Contributor Author

What we need is to remove x11 fonts

The name of the issue implies flatpak can't access host fonts at all, including x11 fonts

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/firefox-flatpak-has-missing-mouse-cursors-in-native-wayland-mode/24321/14

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/how-to-use-fix-for-flatpak-apps-cursor-theme/30893/1

@Svenum
Copy link

Svenum commented Aug 11, 2023

Any updates?

@Guanran928
Copy link
Contributor

Just in case it can be of use to anyone, I made a workaround for this issue using bindfs:

{ config, pkgs, ... }:

{
  system.fsPackages = [ pkgs.bindfs ];
  fileSystems = let
    mkRoSymBind = path: {
      device = path;
      fsType = "fuse.bindfs";
      options = [ "ro" "resolve-symlinks" "x-gvfs-hide" ];
    };
    aggregatedFonts = pkgs.buildEnv {
      name = "system-fonts";
      paths = config.fonts.fonts;
      pathsToLink = [ "/share/fonts" ];
    };
  in {
    # Create an FHS mount to support flatpak host icons/fonts
    "/usr/share/icons" = mkRoSymBind (config.system.path + "/share/icons");
    "/usr/share/fonts" = mkRoSymBind (aggregatedFonts + "/share/fonts");
  };
}

The main trick here is that bindfs resolves all symlink, allowing them to be accessed without letting /nix into the sandbox.

Works perfectly for me!

Is it possible to make it also work with fontconfig? AFAIK fontconfig have the same issue with flatpaks right now.

@PgBiel
Copy link

PgBiel commented Aug 27, 2023

Just in case it can be of use to anyone, I made a workaround for this issue using bindfs:

{ config, pkgs, ... }:

{
  system.fsPackages = [ pkgs.bindfs ];
  fileSystems = let
    mkRoSymBind = path: {
      device = path;
      fsType = "fuse.bindfs";
      options = [ "ro" "resolve-symlinks" "x-gvfs-hide" ];
    };
    aggregatedFonts = pkgs.buildEnv {
      name = "system-fonts";
      paths = config.fonts.fonts;
      pathsToLink = [ "/share/fonts" ];
    };
  in {
    # Create an FHS mount to support flatpak host icons/fonts
    "/usr/share/icons" = mkRoSymBind (config.system.path + "/share/icons");
    "/usr/share/fonts" = mkRoSymBind (aggregatedFonts + "/share/fonts");
  };
}

The main trick here is that bindfs resolves all symlink, allowing them to be accessed without letting /nix into the sandbox.

This seems to trigger an infinite recursion with the mountpoint valid path check and boot.zfs.enabled (...which I never set, and I also don't use ZFS) when there's a filesystem whose name (apparently) begins with a letter or with /v (...very specific, yeah). I include some more detailed info below.

(Not sure if this is an issue with NixOS or with the shared code (or something I messed up)... I could open a separate issue if it's the former.)

Edit: I'm failing to make a properly shareable minimal example, sorry for that - so it's more likely that it's my fault than otherwise. But, in case anyone else stumbles upon this, setting the filesystem name to a known working name - such as /root-var instead of /var - while changing the mountPoint attribute to the actual mount point seems to fix it.

Full stack trace

warning: Git tree '/mnt/etc/nixos' is dirty
evaluating flake...
error:
       … while checking flake output 'nixosConfigurations'

       at /nix/store/2y86pqz62s6v3yjb6qi0hvsxvbx7i3i1-source/flake.nix:29:7:

           28|       # export NixOS configs from there
           29|       nixosConfigurations =
             |       ^
           30|         import ./outputs/nixos-conf.nix { inherit inputs system; };

       … while checking the NixOS configuration 'nixosConfigurations.pgnixospc'

       at /nix/store/2y86pqz62s6v3yjb6qi0hvsxvbx7i3i1-source/outputs/nixos-conf.nix:20:3:

           19|   # Pg NixOS PC
           20|   pgnixospc = mkHost "pgnixospc" ../hosts/pgnixospc {
             |   ^
           21|     users."pgbiel" = {

       … while evaluating call site

       at «none»:0: (source not available)

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:812:24:

          811|     let f = attrPath:
          812|       zipAttrsWith (n: values:
             |                        ^
          813|         let here = attrPath ++ [n]; in

       … while evaluating call site

       at «none»:0: (source not available)

       … while calling 'g'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:595:19:

          594|           g =
          595|             name: value:
             |                   ^
          596|             if isAttrs value && cond value

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:598:20:

          597|               then recurse (path ++ [name]) value
          598|               else f (path ++ [name]) value;
             |                    ^
          599|         in mapAttrs g;

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:248:72:

          247|           # For definitions that have an associated option
          248|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                                                                        ^
          249|

       … while evaluating the attribute 'value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:759:9:

          758|     in warnDeprecation opt //
          759|       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
             |         ^
          760|         inherit (res.defsFinal') highestPrio;

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

       … while evaluating the attribute 'mergedValue'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:794:5:

          793|     # Type-check the remaining definitions, and merge them. Or throw if no definitions.
          794|     mergedValue =
             |     ^
          795|       if isDefined then

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:788:9:

          787|       in {
          788|         values = defs''';
             |         ^
          789|         inherit (defs'') highestPrio;

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:887:7:

          886|     in {
          887|       values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
             |       ^
          888|       inherit highestPrio;

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:17:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                 ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:28:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                            ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

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

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:775:137:

          774|         defs' = concatMap (m:
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
             |                                                                                                                                         ^
          776|         ) defs;

       … while calling 'dischargeProperties'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:846:25:

          845|   */
          846|   dischargeProperties = def:
             |                         ^
          847|     if def._type or "" == "merge" then

       … while evaluating the attribute 'value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:599:44:

          598|       defnsByName' = byName "config" (module: value:
          599|           [{ inherit (module) file; inherit value; }]
             |                                            ^
          600|         ) configs;

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/modules/system/activation/top-level.nix:131:12:

          130|   # Replace runtime dependencies
          131|   system = foldr ({ oldDependency, newDependency }: drv:
             |            ^
          132|       pkgs.replaceDependency { inherit oldDependency newDependency drv; }

       … while calling 'foldr'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:53:20:

           52|   */
           53|   foldr = op: nul: list:
             |                    ^
           54|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:60:8:

           59|         else op (elemAt list n) (fold' (n + 1));
           60|     in fold' 0;
             |        ^
           61|

       … while calling 'fold''

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:56:15:

           55|       len = length list;
           56|       fold' = n:
             |               ^
           57|         if n == len

       … while evaluating call site

       at «none»:0: (source not available)

       … while calling 'g'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:595:19:

          594|           g =
          595|             name: value:
             |                   ^
          596|             if isAttrs value && cond value

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:598:20:

          597|               then recurse (path ++ [name]) value
          598|               else f (path ++ [name]) value;
             |                    ^
          599|         in mapAttrs g;

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:248:72:

          247|           # For definitions that have an associated option
          248|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                                                                        ^
          249|

       … while evaluating the attribute 'value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:759:9:

          758|     in warnDeprecation opt //
          759|       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
             |         ^
          760|         inherit (res.defsFinal') highestPrio;

       … while evaluating the option `assertions':

       … while evaluating the attribute 'mergedValue'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:794:5:

          793|     # Type-check the remaining definitions, and merge them. Or throw if no definitions.
          794|     mergedValue =
             |     ^
          795|       if isDefined then

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:788:9:

          787|       in {
          788|         values = defs''';
             |         ^
          789|         inherit (defs'') highestPrio;

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:887:7:

          886|     in {
          887|       values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
             |       ^
          888|       inherit highestPrio;

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:17:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                 ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:28:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                            ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while evaluating definitions from `/nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/modules/tasks/filesystems/zfs.nix':

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:775:137:

          774|         defs' = concatMap (m:
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
             |                                                                                                                                         ^
          776|         ) defs;

       … while calling 'dischargeProperties'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:846:25:

          845|   */
          846|   dischargeProperties = def:
             |                         ^
          847|     if def._type or "" == "merge" then

       … while evaluating the attribute 'condition'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:951:14:

          950|     { _type = "if";
          951|       inherit condition content;
             |              ^
          952|     };

       … while evaluating the attribute 'condition'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:951:14:

          950|     { _type = "if";
          951|       inherit condition content;
             |              ^
          952|     };

       … while evaluating call site

       at «none»:0: (source not available)

       … while calling 'g'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:595:19:

          594|           g =
          595|             name: value:
             |                   ^
          596|             if isAttrs value && cond value

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:598:20:

          597|               then recurse (path ++ [name]) value
          598|               else f (path ++ [name]) value;
             |                    ^
          599|         in mapAttrs g;

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:248:72:

          247|           # For definitions that have an associated option
          248|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                                                                        ^
          249|

       … while evaluating the attribute 'value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:759:9:

          758|     in warnDeprecation opt //
          759|       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
             |         ^
          760|         inherit (res.defsFinal') highestPrio;

       … while evaluating the option `boot.zfs.enabled':

       … while evaluating the attribute 'mergedValue'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:794:5:

          793|     # Type-check the remaining definitions, and merge them. Or throw if no definitions.
          794|     mergedValue =
             |     ^
          795|       if isDefined then

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:788:9:

          787|       in {
          788|         values = defs''';
             |         ^
          789|         inherit (defs'') highestPrio;

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:784:14:

          783|           # Avoid sorting if we don't have to.
          784|           if any (def: def.value._type or "" == "order") defs''.values
             |              ^
          785|           then sortProperties defs''.values

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:784:19:

          783|           # Avoid sorting if we don't have to.
          784|           if any (def: def.value._type or "" == "order") defs''.values
             |                   ^
          785|           then sortProperties defs''.values

       … while evaluating the attribute 'value._type'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:885:73:

          884|       highestPrio = foldl' (prio: def: min (getPrio def) prio) 9999 defs;
          885|       strip = def: if def.value._type or "" == "override" then def // { value = def.value.content; } else def;
             |                                                                         ^
          886|     in {

       … while evaluating the attribute 'value.content'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:966:14:

          965|     { _type = "override";
          966|       inherit priority content;
             |              ^
          967|     };

       … while evaluating the attribute 'default'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/modules/tasks/filesystems/zfs.nix:217:9:

          216|         type = types.bool;
          217|         default = inInitrd || inSystem;
             |         ^
          218|         defaultText = literalMD "`true` if ZFS filesystem support is enabled";

       … while evaluating call site

       at «none»:0: (source not available)

       … while calling 'g'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:595:19:

          594|           g =
          595|             name: value:
             |                   ^
          596|             if isAttrs value && cond value

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:598:20:

          597|               then recurse (path ++ [name]) value
          598|               else f (path ++ [name]) value;
             |                    ^
          599|         in mapAttrs g;

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:248:72:

          247|           # For definitions that have an associated option
          248|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                                                                        ^
          249|

       … while evaluating the attribute 'value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:759:9:

          758|     in warnDeprecation opt //
          759|       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
             |         ^
          760|         inherit (res.defsFinal') highestPrio;

       … while evaluating the option `boot.initrd.supportedFilesystems':

       … while evaluating the attribute 'mergedValue'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:794:5:

          793|     # Type-check the remaining definitions, and merge them. Or throw if no definitions.
          794|     mergedValue =
             |     ^
          795|       if isDefined then

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:788:9:

          787|       in {
          788|         values = defs''';
             |         ^
          789|         inherit (defs'') highestPrio;

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:887:7:

          886|     in {
          887|       values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
             |       ^
          888|       inherit highestPrio;

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:17:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                 ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:28:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                            ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while evaluating definitions from `/nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/modules/system/boot/stage-1.nix':

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:775:137:

          774|         defs' = concatMap (m:
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
             |                                                                                                                                         ^
          776|         ) defs;

       … while calling 'dischargeProperties'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:846:25:

          845|   */
          846|   dischargeProperties = def:
             |                         ^
          847|     if def._type or "" == "merge" then

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:852:11:

          851|         if def.condition then
          852|           dischargeProperties def.content
             |           ^
          853|         else

       … while calling 'dischargeProperties'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:846:25:

          845|   */
          846|   dischargeProperties = def:
             |                         ^
          847|     if def._type or "" == "merge" then

       … while evaluating the attribute 'content'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:951:14:

          950|     { _type = "if";
          951|       inherit condition content;
             |              ^
          952|     };

       … while evaluating call site

       at «none»:0: (source not available)

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:812:24:

          811|     let f = attrPath:
          812|       zipAttrsWith (n: values:
             |                        ^
          813|         let here = attrPath ++ [n]; in

       … while evaluating call site

       at «none»:0: (source not available)

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/types.nix:563:29:

          562|       merge = loc: defs:
          563|         zipAttrsWith (name: defs:
             |                             ^
          564|           let merged = mergeDefinitions (loc ++ [name]) elemType defs;

       … while evaluating the attribute 'optionalValue.value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:806:5:

          805|
          806|     optionalValue =
             |     ^
          807|       if isDefined then { value = mergedValue; }

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:788:9:

          787|       in {
          788|         values = defs''';
             |         ^
          789|         inherit (defs'') highestPrio;

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:887:7:

          886|     in {
          887|       values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
             |       ^
          888|       inherit highestPrio;

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:17:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                 ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:28:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                            ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while evaluating definitions from `/nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/modules/tasks/filesystems.nix':

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:775:137:

          774|         defs' = concatMap (m:
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
             |                                                                                                                                         ^
          776|         ) defs;

       … while calling 'dischargeProperties'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:846:25:

          845|   */
          846|   dischargeProperties = def:
             |                         ^
          847|     if def._type or "" == "merge" then

       … while evaluating the attribute 'value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/types.nix:569:58:

          568|         # Push down position info.
          569|         (map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs);
             |                                                          ^
          570|       emptyValue = { value = {}; };

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:88:39:

           87|         then value
           88|         else { ${elemAt attrPath n} = atDepth (n + 1); };
             |                                       ^
           89|     in atDepth 0;

       … while calling 'atDepth'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:85:17:

           84|       len = length attrPath;
           85|       atDepth = n:
             |                 ^
           86|         if n == len

       … while evaluating the attribute 'value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:599:44:

          598|       defnsByName' = byName "config" (module: value:
          599|           [{ inherit (module) file; inherit value; }]
             |                                            ^
          600|         ) configs;

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/modules/tasks/filesystems.nix:14:18:

           13|
           14|   fileSystems' = toposort fsBefore (attrValues config.fileSystems);
             |                  ^
           15|

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:470:18:

          469|       dfsthis = listDfs true before list;
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
             |                  ^
          471|     in

       … while calling 'toposort'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:467:22:

          466|    */
          467|   toposort = before: list:
             |                      ^
          468|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:469:17:

          468|     let
          469|       dfsthis = listDfs true before list;
             |                 ^
          470|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);

       … while calling 'listDfs'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:428:35:

          427|    */
          428|   listDfs = stopOnCycles: before: list:
             |                                   ^
          429|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:443:8:

          442|                           (tail b.right ++ b.wrong);
          443|     in dfs' (head list) [] (tail list);
             |        ^
          444|

       … while calling 'dfs''

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:430:27:

          429|     let
          430|       dfs' = us: visited: rest:
             |                           ^
          431|         let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:433:15:

          432|           c = filter (x: before x us) visited;
          433|           b = partition (x: before x us) rest;
             |               ^
          434|         in if stopOnCycles && (length c > 0)

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:433:26:

          432|           c = filter (x: before x us) visited;
          433|           b = partition (x: before x us) rest;
             |                          ^
          434|         in if stopOnCycles && (length c > 0)

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/lists.nix:433:29:

          432|           c = filter (x: before x us) visited;
          433|           b = partition (x: before x us) rest;
             |                             ^
          434|         in if stopOnCycles && (length c > 0)

       … while calling 'fsBefore'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/lib/utils.nix:17:17:

           16|   # Check whenever `b` depends on `a` as a fileSystem
           17|   fsBefore = a: b:
             |                 ^
           18|     let

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/lib/utils.nix:38:8:

           37|
           38|     in hasPrefix a'.mountPoint b'.device
             |        ^
           39|     || hasPrefix a'.mountPoint b'.mountPoint

       … while calling 'hasPrefix'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/strings.nix:258:5:

          257|     # Input string
          258|     str:
             |     ^
          259|     # Before 23.05, paths would be copied to the store before converting them

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/strings.nix:261:5:

          260|     # to strings and comparing. This was surprising and confusing.
          261|     warnIf
             |     ^
          262|       (isPath pref)

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/trivial.nix:357:50:

          356|   */
          357|   warnIf = cond: msg: if cond then warn msg else x: x;
             |                                                  ^
          358|

       … while evaluating the attribute 'device'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/lib/utils.nix:30:37:

           29|       normalisePath = path: "${path}${optionalString (!(hasSuffix "/" path)) "/"}";
           30|       normalise = mount: mount // { device = normalisePath (toString mount.device);
             |                                     ^
           31|                                     mountPoint = normalisePath mount.mountPoint;

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/lib/utils.nix:30:46:

           29|       normalisePath = path: "${path}${optionalString (!(hasSuffix "/" path)) "/"}";
           30|       normalise = mount: mount // { device = normalisePath (toString mount.device);
             |                                              ^
           31|                                     mountPoint = normalisePath mount.mountPoint;

       … while calling 'normalisePath'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/lib/utils.nix:29:23:

           28|       # though, this is not a problem: "/aaa/" is not a prefix of "/aaaa/".
           29|       normalisePath = path: "${path}${optionalString (!(hasSuffix "/" path)) "/"}";
             |                       ^
           30|       normalise = mount: mount // { device = normalisePath (toString mount.device);

       … while evaluating call site

       at «none»:0: (source not available)

       … while calling 'g'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:595:19:

          594|           g =
          595|             name: value:
             |                   ^
          596|             if isAttrs value && cond value

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:598:20:

          597|               then recurse (path ++ [name]) value
          598|               else f (path ++ [name]) value;
             |                    ^
          599|         in mapAttrs g;

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:248:72:

          247|           # For definitions that have an associated option
          248|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                                                                        ^
          249|

       … while evaluating the attribute 'value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:759:9:

          758|     in warnDeprecation opt //
          759|       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
             |         ^
          760|         inherit (res.defsFinal') highestPrio;

       … while evaluating the option `fileSystems."/usr/share/icons".device':

       … while evaluating the attribute 'mergedValue'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:794:5:

          793|     # Type-check the remaining definitions, and merge them. Or throw if no definitions.
          794|     mergedValue =
             |     ^
          795|       if isDefined then

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:788:9:

          787|       in {
          788|         values = defs''';
             |         ^
          789|         inherit (defs'') highestPrio;

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:887:7:

          886|     in {
          887|       values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
             |       ^
          888|       inherit highestPrio;

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:17:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                 ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:28:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                            ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while evaluating definitions from `/nix/store/2y86pqz62s6v3yjb6qi0hvsxvbx7i3i1-source/system/x11/fonts/fhs-workaround.nix':

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:775:137:

          774|         defs' = concatMap (m:
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
             |                                                                                                                                         ^
          776|         ) defs;

       … while calling 'dischargeProperties'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:846:25:

          845|   */
          846|   dischargeProperties = def:
             |                         ^
          847|     if def._type or "" == "merge" then

       … while evaluating the attribute 'value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:599:44:

          598|       defnsByName' = byName "config" (module: value:
          599|           [{ inherit (module) file; inherit value; }]
             |                                            ^
          600|         ) configs;

       … while evaluating the attribute 'passAsFile' of the derivation 'system-path'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/pkgs/stdenv/generic/make-derivation.nix:303:7:

          302|     // (lib.optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) {
          303|       name =
             |       ^
          304|         let

       … while evaluating the attribute 'passAsFile'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/pkgs/build-support/buildenv/default.nix:77:5:

           76|     # XXX: The size is somewhat arbitrary
           77|     passAsFile = if builtins.stringLength pkgs >= 128*1024 then [ "pkgs" ] else [ ];
             |     ^
           78|   }

       … while evaluating call site

       at «none»:0: (source not available)

       … while calling 'g'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:595:19:

          594|           g =
          595|             name: value:
             |                   ^
          596|             if isAttrs value && cond value

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/attrsets.nix:598:20:

          597|               then recurse (path ++ [name]) value
          598|               else f (path ++ [name]) value;
             |                    ^
          599|         in mapAttrs g;

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:248:72:

          247|           # For definitions that have an associated option
          248|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                                                                        ^
          249|

       … while evaluating the attribute 'value'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:759:9:

          758|     in warnDeprecation opt //
          759|       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
             |         ^
          760|         inherit (res.defsFinal') highestPrio;

       … while evaluating the option `environment.systemPackages':

       … while evaluating the attribute 'mergedValue'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:794:5:

          793|     # Type-check the remaining definitions, and merge them. Or throw if no definitions.
          794|     mergedValue =
             |     ^
          795|       if isDefined then

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:788:9:

          787|       in {
          788|         values = defs''';
             |         ^
          789|         inherit (defs'') highestPrio;

       … while evaluating the attribute 'values'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:887:7:

          886|     in {
          887|       values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
             |       ^
          888|       inherit highestPrio;

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:17:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                 ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while calling anonymous lambda

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:774:28:

          773|         # Process mkMerge and mkIf properties.
          774|         defs' = concatMap (m:
             |                            ^
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

       … while evaluating definitions from `/nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/nixos/modules/tasks/filesystems/zfs.nix':

       … while evaluating call site

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:775:137:

          774|         defs' = concatMap (m:
          775|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
             |                                                                                                                                         ^
          776|         ) defs;

       … while calling 'dischargeProperties'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:846:25:

          845|   */
          846|   dischargeProperties = def:
             |                         ^
          847|     if def._type or "" == "merge" then

       … while evaluating the attribute 'condition'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:951:14:

          950|     { _type = "if";
          951|       inherit condition content;
             |              ^
          952|     };

       … while evaluating the attribute 'condition'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:951:14:

          950|     { _type = "if";
          951|       inherit condition content;
             |              ^
          952|     };

       … while evaluating the attribute 'condition'

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:951:14:

          950|     { _type = "if";
          951|       inherit condition content;
             |              ^
          952|     };

       error: infinite recursion encountered

       at /nix/store/pbh1jb88mn0sy1rw0azn9jml172pxjpq-source/lib/modules.nix:951:14:

          950|     { _type = "if";
          951|       inherit condition content;
             |              ^
          952|     };

system/x11/fonts/fhs-workaround.nix (with pgopts.x11.fonts.fhs-workaround.enable = true; elsewhere)

Verbatim copy of the suggested code:

# Workaround for fonts and icons in weird locations

{ pkgs, config, lib, ... }:

let
  # just make our config path shorter
  cfg = config.pgopts.x11.fonts;
in
{
  options.pgopts.x11.fonts.fhs-workaround.enable = lib.mkEnableOption "Enable workaround for FHS locations for fonts (and icons).";

  config = lib.mkIf (cfg.enable && cfg.fhs-workaround.enable) {
    # bindfs is basically mount --bind
    system.fsPackages = [ pkgs.bindfs ];

    fileSystems =
      let
        mkRoSymBind = path: {
          device = path;
          fsType = "fuse.bindfs";
          # resolve-symlinks => no more fonts as symlinks
          options = [ "ro" "resolve-symlinks" "x-gvfs-hide" ];
        };
        aggregatedFonts = pkgs.buildEnv {
          name = "system-fonts";
          paths = config.fonts.fonts;
          pathsToLink = [ "/share/fonts" ];
        };
      in
      {
        # Create an FHS mount to support flatpak host icons/fonts
        "/usr/share/icons" = mkRoSymBind (config.system.path + "/share/icons");
        "/usr/share/fonts" = mkRoSymBind (aggregatedFonts + "/share/fonts");
      };
  };
}

Hardware config

Commenting out the line with /var makes the error stop. (Conversely, the error still happens when all lines but the /var one are commented out.)
Replacing it with e.g. fileSystems."/var" = {}; or fileSystems."/var/log" = {}; or fileSystems."/v" = root-subvolume "var"; or fileSystems."var" = {}; or fileSystems."root-var" = root-subvolume "var" // { mountPoint = "/var"; }; does not fix the error. Removing /v or adding a slash before the r (or other letters) in root in the name fixes it.

# PC hardware config (AMD CPU & AMD GPU)
{ config, lib, pkgs, modulesPath, ... }:

let
  home-subvolume = name:
    {
      device = "/dev/disk/by-label/NIXHOME";
      fsType = "btrfs";
      options = [ "compress-force=zstd" "discard=async" "subvol=${name}" ];
      depends = [ "/home" ];
    };

  root-subvolume = name:
    {
      device = "/dev/disk/by-label/NIXROOT";
      fsType = "btrfs";
      options = [ "compress-force=zstd" "discard=async" "noatime" "subvol=${name}" ];
    };
in
{
  imports =
    [
      (modulesPath + "/installer/scan/not-detected.nix")
    ];

  boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "usbhid" "sd_mod" ];
  boot.initrd.kernelModules = [ "dm-snapshot" ];
  boot.kernelModules = [ "kvm-amd" ];
  boot.extraModulePackages = [ ];

  # Root subvolumes
  fileSystems."/" = root-subvolume "root";
  fileSystems."/nix" = root-subvolume "nix";
  fileSystems."/var" = root-subvolume "var";  # <--- "bad" line
  fileSystems."/.snapshots" = root-subvolume ".snapshots";

  # Home subvolumes
  fileSystems."/home" = home-subvolume "homeroot" // {
    depends = [ ];

    # not decrypted by grub => specify here
    # (we use keyfile to not require double passphrase)
    encrypted = {
      enable = true;
      label = "crypthome";
      blkDev = "/dev/disk/by-partlabel/NIXENCHOME";
      # keyFile already specified by configuration.nix
    };
  };

  # for the filesystems below, 'crypthome' will already be decrypted,
  # so no need to specify 'encrypted' again
  fileSystems."/home/pgbiel" = home-subvolume "pgbiel";
  fileSystems."/home/pgbiel/Downloads" = home-subvolume "pgbiel/Downloads";
  fileSystems."/home/pgbiel/.cache" = home-subvolume "pgbiel/.cache";
  fileSystems."/home/pgbiel/.snapshots" = home-subvolume "pgbiel/.snapshots";

  # ESP / EFI boot partition
  fileSystems."/boot/efi" =
    {
      device = "/dev/disk/by-label/NIXBOOT";
      fsType = "vfat";
    };

  # Old SUSE /home partition (data storage)
  fileSystems."/mnt/susehome" =
    {
      device = "/dev/disk/by-label/SUSEHOME";
      fsType = "btrfs";
      options = [ "compress-force=zstd:3" ];
      encrypted = {
        enable = true;
        label = "cryptsusehome";
        blkDev = "/dev/disk/by-partlabel/SUSEENCHOME";
        # keyFile already specified by configuration.nix
      };
    };

  # decrypt / partition before boot
  boot.initrd.luks.devices."cryptroot" = {
    device = "/dev/disk/by-partlabel/NIXENCROOT";

    # this is a LVM partition we're decrypting
    preLVM = true;

    # allow TRIM support, if the SSD supports it
    allowDiscards = true;

    # keyFile is already specified by configuration.nix
  };

  swapDevices =
    [{ device = "/dev/disk/by-label/NIXSWAP"; }];

  # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
  # (the default) this is the recommended approach. When using systemd-networkd it's
  # still possible to use this option, but it's recommended to use it in conjunction
  # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
  networking.useDHCP = lib.mkDefault true;
  # networking.interfaces.enp4s0.useDHCP = lib.mkDefault true;
  # networking.interfaces.wlp3s0.useDHCP = lib.mkDefault true;

  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
  hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

@Guanran928
Copy link
Contributor

This seems to trigger an infinite recursion with the mountpoint valid path check and boot.zfs.enabled

Hmm, today I got the same error while rebuilding my system, thought the issue was on my side since I changed a lot of stuff in my config.

@ilya-fedin
Copy link
Contributor Author

Is it possible to make it also work with fontconfig?

That's not a good idea. Those configs are incompatible between fontconfig versions.

@andrevmatos
Copy link
Member

@PgBiel I'm also experiencing this same issue; from what I see, this happens when the icons bind mountpoint tries to reference config.system.path, which fail to evaluate due to it trying to sort the config.fileSystems (couldn't debug why /v prefix makes it fail though). For me, it's hard to implement the workaround of renaming the key and adding mountPoint property, as most of my /v filesystems are /var folders mounted by impermanence's directories option, which doesn't give an option to rename key and add the mount point.

Another very ugly, impure approach is to replace config.system.path + "/share/icons" with /run/current-system/sw/share/icons; notice this doesn't update upon new system activation, so one needs to remount icons manually if willing to reload that mountpoint, but it removes the config.system.path evaluation and fix the infinite recursion.

@alaviss
Copy link
Contributor

alaviss commented Sep 10, 2023

this happens when the icons bind mountpoint tries to reference config.system.path, which fail to evaluate due to it trying to sort the config.fileSystems

If config.system.path doesn't work, then try building a custom env instead, like this:

aggregatedIcons = pkgs.buildEnv {
  name = "system-icons";
  paths = config.environment.systemPackages;
  pathsToLink = [ "/share/icons" ];
};

Then refer to it instead.

@Guanran928
Copy link
Contributor

After a quick search, I found this.

I am not a developer, can anyone tell me can this option be used?

@alaviss
Copy link
Contributor

alaviss commented Sep 10, 2023

Unfortunately not, you can use it to change the path bind mounted by flatpak but it won't work on NixOS because an aggregated folder is a set of symlinks and regular bind mounting won't resolve them automatically

@jacobranson
Copy link

this happens when the icons bind mountpoint tries to reference config.system.path, which fail to evaluate due to it trying to sort the config.fileSystems

If config.system.path doesn't work, then try building a custom env instead, like this:

aggregatedIcons = pkgs.buildEnv {
  name = "system-icons";
  paths = config.environment.systemPackages;
  pathsToLink = [ "/share/icons" ];
};

Then refer to it instead.

This also does not work, at least from what I can see. Infinite recursion error remains.

@jacobranson
Copy link

This is the best I could come up with as someone hitting the infinite recursion issue. I have replaced the config.fonts.fonts with config.fonts.packages since that is the new default coming in NixOS 23.11 (and currently in NixOS Unstable). I have also hardcoded the packages with icons that Firefox needs in Flatpak (the cursor from GNOME and one for KDE, which I don't use). Additionally, I have updated the bind mount path to /usr/local/share/fonts, since, for some reason, Firefox was not finding them at /usr/share/fonts. Interestingly, if I try to do the same with the icons folder, it doesn't work.

This works for the Firefox Flatpak, but the VSCodium Flatpak does not use the cursor icons for some reason.

Anyway, hope this helps someone. Really looking forward to seeing if someone can crack the case on that infinite recursion error, to remove this package hardcoding workaround from this Flatpak workaround!

{
  system.fsPackages = [ pkgs.bindfs ];
  fileSystems = let
    mkRoSymBind = path: {
      device = path;
      fsType = "fuse.bindfs";
      options = [ "ro" "resolve-symlinks" "x-gvfs-hide" ];
    };
    aggregatedIcons = pkgs.buildEnv {
      name = "system-icons";
      paths = with pkgs; [
        #libsForQt5.breeze-qt5  # for plasma
        gnome.gnome-themes-extra
      ];
      pathsToLink = [ "/share/icons" ];
    };
    aggregatedFonts = pkgs.buildEnv {
      name = "system-fonts";
      paths = config.fonts.packages;
      pathsToLink = [ "/share/fonts" ];
    };
  in {
    "/usr/share/icons" = mkRoSymBind "${aggregatedIcons}/share/icons";
    "/usr/local/share/fonts" = mkRoSymBind "${aggregatedFonts}/share/fonts";
  };

  fonts = {
    fontDir.enable = true;
    packages = with pkgs; [
      noto-fonts
      noto-fonts-emoji
      noto-fonts-cjk
    ];
  };
}

@FliegendeWurst
Copy link
Member

PR with fix: #262462 (posting here to notify issue subscribers)

@AndreiSva
Copy link

I've been having this issue with flatpak element. the cursor is the default wayland cursor instead of the gnome one.

@beh-10257
Copy link

hopefully this gets fixed soon

@beh-10257
Copy link

beh-10257 commented Mar 10, 2024

{ config, pkgs, ... }:
let
home-path = ''${config.home-manager.users.your_user_name.home.path}'';
home-files = ''${config.home-manager.users.your_user_name.home-files}'';
icon_theme_name = ''${config.home-manager.users.your_user_name.gtk.iconTheme.name}'';
theme_name = ''${config.home-manager.users.your_user_name.gtk.theme.name}'';
cursor_theme_name= ''${config.home-manager.users.your_user_name.gtk.cursorTheme.name}'';
in
let
gawk=''${pkgs.gawk}/bin/gawk'';
flatpak=''${pkgs.flatpak}/bin/flatpak'';
su=''${pkgs.su}/bin/su'';
cursor_theme_path=''${home-files}/.local/share/icons/${cursor_theme_name}'';
icon_theme_path=''${home-files}/.local/share/icons/${icon_theme_name}'';
theme_path=''${home-files}/.local/share/themes/${theme_name}'';
icon_theme_path_1=''${home-path}/share/icons/${icon_theme_name}'';
theme_path_1=''${home-path}/share/themes/${theme_name}'';
in
let
workaround =
''
${gawk} -i inplace '{gsub(/\/nix\/store[^;]*;|!\/nix\/store[^;]*;/,""); print}' /var/lib/flatpak/overrides/global
${gawk} -i inplace '{gsub(/filesystems=/,"filesystems=${icon_theme_path};${theme_path};${cursor_theme_path};"); print}' /var/lib/flatpak/overrides/global
${gawk} -i inplace '{gsub(/\/nix\/store[^;]*;|!\/nix\/store[^;]*;/,""); print}' /home/your_user_name/.local/share/flatpak/overrides/global
${gawk} -i inplace '{gsub(/filesystems=/,"filesystems=${icon_theme_path};${theme_path};${cursor_theme_path};"); print}' /home/your_user_name/.local/share/flatpak/overrides/global
${flatpak} override --env=GTK_THEME=${theme_name}
${su} your_user_name -c '${flatpak} override --user --env=GTK_THEME=${theme_name}'
'';
in
{
  services = {
    flatpak = {
      enable = true;
    };
  };
  ##workaround for themes and icons
  systemd.services.workaround-for-theme-and-icons = {
      wantedBy = ["multi-user.target"];
      after = ["systemd-user-sessions.service"] ;
      before = ["getty.target"] ;
      script = workaround;
  };
  home-manager.users.your_user_name.home.file = {
    ".local/share/icons/${icon_theme_name}".source = config.home-manager.users.your_user_name.lib.file.mkOutOfStoreSymlink "${icon_theme_path_1}";
    ".local/share/themes/${theme_name}".source = config.home-manager.users.your_user_name.lib.file.mkOutOfStoreSymlink "${theme_path_1}";
  };
}

since soon isn't coming anytime soon it seems here's my temporary fix
(this doesn't support fonts since I have no idea how to do so)

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/system-cursor-theme-and-scaling-not-being-followed-by-some-apps-system-packages-flatpak/49917/4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
0.kind: bug Something is broken
Projects
None yet