Back to skills
SKILL.md
Nix Platform Specific Options
ASecurityWrite Nix modules with platform-specific options (NixOS vs Darwin) without infinite recursion. Use when mkIf causes evaluation errors or options don't exist across platforms.
- 81 stars
- 0 votes
- 0 copies
- 0 views
- Added September 8, 2026
Security analysis
100/100npx -y skills add edmundmiller/dotfiles --skill nix-platform-specific-options --agent claude-codeAre you the author of Nix Platform Specific Options?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/edmundmiller-nix-platform-specific-options)---
name: nix-platform-specific-options
description: Write Nix modules with platform-specific options (NixOS vs Darwin) without infinite recursion. Use when mkIf causes evaluation errors or options don't exist across platforms.
---
# Nix Module Platform-Specific Options
## Overview
When writing Nix modules that need to hide platform-specific options (NixOS vs Darwin), using `mkIf` alone causes infinite recursion. This skill documents the correct pattern.
## The Problem
`mkIf` is evaluated lazily but the option **path** is still visible during module evaluation. This causes errors like:
```
error: The option `users.defaultUserShell' does not exist.
```
Or infinite recursion when `config` is referenced in option defaults or `optionalAttrs` conditions.
## The Pattern
**Use `optionalAttrs` for platform checks, `mkIf` for config-dependent checks.**
| Check Type | Tool | Evaluated |
| ------------------------------------------ | --------------- | ---------- |
| Platform (`isDarwin`, `!isDarwin`) | `optionalAttrs` | Parse time |
| Config values (`cfg.enable`, `cfg.flavor`) | `mkIf` | Lazy |
## Examples
### ❌ Wrong: mkIf for platform check
```nix
config = mkIf (!isDarwin) {
users.defaultUserShell = pkgs.zsh; # Darwin sees this path!
};
```
### ✅ Correct: optionalAttrs for platform check
```nix
config = optionalAttrs (!isDarwin) {
users.defaultUserShell = pkgs.zsh; # Hidden from Darwin
};
```
### ❌ Wrong: Config value in optionalAttrs condition
```nix
# cfg.flavor evaluated at parse time → infinite recursion
(optionalAttrs (isDarwin && cfg.flavor == "personal") {
services.onepassword-secrets.enable = true;
})
```
### ✅ Correct: Nest mkIf inside optionalAttrs
```nix
# Platform check at parse time, config check lazy
(optionalAttrs isDarwin (mkIf (cfg.flavor == "personal") {
services.onepassword-secrets.enable = true;
}))
```
### ❌ Wrong: config reference in option default
```nix
options.modules.foo = {
user = mkOpt types.str config.user.name; # Infinite recursion!
};
```
### ✅ Correct: Static default, use config in config section
```nix
options.modules.foo = {
user = mkOpt types.str null;
};
config = mkIf cfg.enable (let
user = if cfg.user != null then cfg.user else config.user.name;
in {
# Use 'user' variable here
});
```
## Combined Pattern
For modules with both platform-specific options AND config-dependent behavior:
```nix
config = mkIf cfg.enable (mkMerge [
# Common config (all platforms)
{ /* ... */ }
# Darwin-only options
(optionalAttrs isDarwin {
programs.zsh.interactiveShellInit = "...";
})
# NixOS-only options
(optionalAttrs (!isDarwin) {
users.defaultUserShell = pkgs.zsh;
})
# Darwin + config-dependent (nested)
(optionalAttrs isDarwin (mkIf (cfg.flavor == "personal") {
services.onepassword-secrets.enable = true;
}))
]);
```
## Quick Reference
| Scenario | Pattern |
| -------------------------- | ---------------------------------------------------------- |
| NixOS-only option | `optionalAttrs (!isDarwin) { ... }` |
| Darwin-only option | `optionalAttrs isDarwin { ... }` |
| Platform + enable check | `optionalAttrs isDarwin (mkIf cfg.enable { ... })` |
| Platform + config value | `optionalAttrs isDarwin (mkIf (cfg.foo == "bar") { ... })` |
| Option default from config | Use `null` default, resolve in `config` section |
## Debugging
When you see infinite recursion errors mentioning `_module.freeformType` or `anon-43`:
1. Search for `config.` references in option defaults
2. Search for `cfg.` references in `optionalAttrs` conditions
3. Search for `mkIf (!isDarwin)` or `mkIf isDarwin` guarding platform-specific options
```bash
# Find problematic patterns
grep -rn "mkOpt.*config\." modules/
grep -rn "optionalAttrs.*cfg\." modules/
grep -rn "mkIf.*isDarwin" modules/
```
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.