Having trouble with custom options

I’m using a nix flake and am setting the packages.

I split the packages into multiple files, one for gui packages and one for important pacakages.

I’m using lib.lists.optionals to make each addition optional but when I rebuild my system all of my packages are gone.

lib.lists.optional documentation: lib.lists: list manipulation functions | nixpkgs

pkgsMain.nix:

{
  config,
  pkgs,
  lib,
  inputs,
  ...
}: let
  guiPackages = import ./guiPackages.nix {inherit pkgs inputs;};
  basePackages = import ./basePackages.nix {inherit pkgs;};
in {
  options = {
    packages.guiPackages.enable = lib.mkEnableOption {
      description = "Enables GUI Packages";
      default = true;
    };

    packages.basePackages.enable = lib.mkEnableOption {
      description = "Enbles base Packages";
      default = true;
    };
  };

  config = {
    environment.systemPackages = [] 
      ++ lib.lists.optional config.packages.guiPackages.enable guiPackages.guiPackages
      ++ lib.lists.optional config.packages.basePackages.enable basePackages.basePackages;
  };
}

From a glance, things look right. What issue do you have exactly?

If guiPackages.guiPackages is a list, you should use lib.lists.optionalslib.lists.optional wraps the value in a list, resulting in a list nested in a list. But I would expect that to just fail.

But it is hard to say what is happening without error or seeing guiPackages definition and your configuration (how you are importing pkgsMain.nix).

I think environment.systemPackages flattens the list.

using lib.lists.optionals did not help.

guiPackages:

{pkgs}: {
  basePackages = with pkgs; [
    eza
    wget
    git
    cmus
    mpv
    killall
    libarchive
    sshfs
    bat
    ffmpeg
    nil
    nh
    neovim
    nitch
    nix-output-monitor
    himalaya
    fuse
    fzf
    unzip
  ];
}

basePackages:

{pkgs}: {
  basePackages = with pkgs; [
    eza
    wget
    git
    cmus
    mpv
    killall
    libarchive
    sshfs
    bat
    ffmpeg
    nil
    nh
    neovim
    nitch
    nix-output-monitor
    himalaya
    fuse
    fzf
    unzip
  ];
}

Directory structure:

flake.nix
flake.lock
modules/
    default.nix // imports ./packages/default.nix
    packages/
        guiPackages.nix
        basePackages.nix
        pkgsMain.nix
        default.nix // imports pkgsMain.nix

something relevant could be that in other situations a similar thing happens
when i added an option to my steam configuration it uninstalled steam:

{
  config,
  lib,
  ...
}: {
  options = {
    steam.enable = lib.mkEnableOption {
      description = "Enables steam";
      default = true;
    };
  };

  config = lib.mkIf config.steam.enable {
    programs.steam = {
      enable = true;
      remotePlay.openFirewall = true;
      dedicatedServer.openFirewall = true;
      gamescopeSession.enable = true;
    };
  };
}

Edit:
in some other situations it does work though
e.g. grub.nix:

{
  config,
  pkgs,
  lib,
  ...
}: {
  options = {
    grub.enable = lib.mkEnableOption {
      description = "enables grub";
      default = true;
    };
  };

  config = lib.mkIf config.grub.enable {
    boot = {
      bootspec.enable = true;
      loader = {
        efi = {canTouchEfiVariables = true;};

        grub = {
          enable = true;
          devices = ["nodev"];
          efiSupport = true;
          configurationLimit = 10;
          theme = pkgs.sleek-grub-theme.override {
            withStyle = "dark";
            withBanner = "Welcome to ${config.networking.hostName}";
          };
        };

        timeout = 5;
      };

      kernelPackages = pkgs.linuxPackages_latest;
      initrd = {kernelModules = ["amdgpu"];};
    };
  };
}

I’m confused how this isn’t causing errors. mkEnableOption doesn’t take an attrset of args like description or default. It just takes a string labeling what would be enabled, and if you want to change the default, you have to do // { default = true; } i.e. it should be like this:

    packages.guiPackages.enable = lib.mkEnableOption "GUI Packages" // {
      default = true;
    };

    packages.basePackages.enable = lib.mkEnableOption "base Packages" // {
      default = true;
    };

What you did should have given you the error message:

error:
       … while evaluating a path segment

         at /nix/store/iqxdbcjlg9wx8gdc8bdhy9nsd5imcbjp-source/lib/options.nix:103:38:

          102|     example = true;
          103|     description = "Whether to enable ${name}.";
             |                                      ^
          104|     type = lib.types.bool;

       error: cannot coerce a set to a string

I’m thinking you’re not even importing your module?

1 Like

Thank you!

I was importing it, but somehow not inserting the name only sometimes doesn’t work, and when I didn’t insert the name, it was just defaulting to false I guess???

I can’t really say that makes sense :stuck_out_tongue: When you call it wrong, it should just straight up throw an error no matter what lol.

That’s not a thing, if something fails it always fails.

What happened is that passing a set to lib.mkEnableOption works fine because there’s no type-checking. But when something in the module system tries to access the description of that option it fails. Nix is lazy, etc.

And the default of false is because mkEnableOption always defaults to false.

1 Like

Thank you for clarifying :slight_smile:

Given there’s a shocking 600+ usages of mkEnableOption with a set argument, seems about time to send a PR to type-check the function.

1 Like