Bug Description
When using programs.openclaw.config = {} (default, empty) or instances.<name>.config = {},
the baseConfig values (e.g. gateway.mode = "local") are silently discarded from the
generated openclaw.json.
Root Cause
In nix/modules/home-manager/openclaw/config.nix (around line 101):
mergedConfig0 = stripNulls (
lib.recursiveUpdate (lib.recursiveUpdate baseConfig cfg.config) inst.config
);
Both cfg.config and inst.config are declared as lib.types.submodule { options = openclawLib.generatedConfigOptions; }.
When set to {}, Nix evaluates all schema options to their defaults — most of which are
null. The resulting attrset looks like:
{ "gateway": { "mode": null, "port": null, ... }, "channels": null, ... }
Then lib.recursiveUpdate baseConfig cfg.config deep-merges this into baseConfig.
Since cfg.config.gateway.mode = null exists as a key, it overwrites
baseConfig.gateway.mode = "local". The outer stripNulls then removes the null,
so gateway.mode disappears from the final JSON entirely.
Affected Versions
Reproduced on commit 0f4d0666f1490cbe1ba76062275eb22d2f89cc44.
Reproduction
programs.openclaw = {
enable = true;
# config = {} is the default — no explicit config set
};
Generated ~/.openclaw/openclaw.json will not contain gateway.mode.
Expected: { "gateway": { "mode": "local" } }.
Proposed Fix
Apply stripNulls to each source before the recursive merge, so null-defaulted schema
options don't shadow baseConfig values:
# Before:
mergedConfig0 = stripNulls (
lib.recursiveUpdate (lib.recursiveUpdate baseConfig cfg.config) inst.config
);
# After:
mergedConfig0 = stripNulls (
lib.recursiveUpdate (lib.recursiveUpdate baseConfig (stripNulls cfg.config)) (stripNulls inst.config)
);
stripNulls is already defined in the same file, so this is a one-line change with no new helpers needed.
Workaround
Patch the generated config via jq in ExecStartPre:
jq '.gateway.mode = "local"' ~/.openclaw/openclaw.json > /tmp/openclaw-patched.json
Bug Description
When using
programs.openclaw.config = {}(default, empty) orinstances.<name>.config = {},the
baseConfigvalues (e.g.gateway.mode = "local") are silently discarded from thegenerated
openclaw.json.Root Cause
In
nix/modules/home-manager/openclaw/config.nix(around line 101):Both
cfg.configandinst.configare declared aslib.types.submodule { options = openclawLib.generatedConfigOptions; }.When set to
{}, Nix evaluates all schema options to their defaults — most of which arenull. The resulting attrset looks like:{ "gateway": { "mode": null, "port": null, ... }, "channels": null, ... }Then
lib.recursiveUpdate baseConfig cfg.configdeep-merges this intobaseConfig.Since
cfg.config.gateway.mode = nullexists as a key, it overwritesbaseConfig.gateway.mode = "local". The outerstripNullsthen removes the null,so
gateway.modedisappears from the final JSON entirely.Affected Versions
Reproduced on commit
0f4d0666f1490cbe1ba76062275eb22d2f89cc44.Reproduction
Generated
~/.openclaw/openclaw.jsonwill not containgateway.mode.Expected:
{ "gateway": { "mode": "local" } }.Proposed Fix
Apply
stripNullsto each source before the recursive merge, so null-defaulted schemaoptions don't shadow
baseConfigvalues:stripNullsis already defined in the same file, so this is a one-line change with no new helpers needed.Workaround
Patch the generated config via
jqinExecStartPre: