zeroclaw-labs/zeroclaw · warning · DiagItem
gateway.web_dist_dir = "{$path}" — {$reason}; gateway.web_di
Error message
gateway.web_dist_dir = "{$path}" — {$reason}; gateway.web_dist_dir is read verbatim, so expand the value yourself (e.g. an absolute path) What it means
A `zeroclaw doctor` warning from `check_web_dist_dir`: `gateway.web_dist_dir` starts with `~` or contains `$`, i.e. it looks like it expects shell expansion. The gateway reads the value verbatim and performs no expansion, so the dashboard static files would be served from a literally-named (nonexistent) directory. The localized message is built from the Fluent keys `cli-doctor-web-dist-dir-expansion-warning` plus a reason key (`cli-web-dist-dir-reason-tilde` or `cli-web-dist-dir-reason-dollar`).
Source
Thrown at crates/zeroclaw-runtime/src/doctor/mod.rs:1400
cat,
format!("{} (at {})", warning.message, warning.path),
));
}
}
fn check_web_dist_dir(config: &Config, items: &mut Vec<DiagItem>) {
let cat = "config";
match config.gateway.web_dist_dir.as_deref() {
None => {}
Some(value) => match web_dist_dir_expansion_reason_key(value) {
None => {}
Some(reason_key) => {
let reason = crate::i18n::get_required_cli_string(reason_key);
let message = crate::i18n::get_required_cli_string_with_args(
"cli-doctor-web-dist-dir-expansion-warning",
&[("path", value), ("reason", reason.as_str())],
);
items.push(DiagItem::warn(cat, message));
}
},
}
}
/// Return the Fluent reason key when `value` looks like it expects
/// shell expansion the gateway will not perform. `None` means the value
/// is a literal path that the gateway can resolve as-is.
fn web_dist_dir_expansion_reason_key(value: &str) -> Option<&'static str> {
if value.starts_with('~') {
Some("cli-web-dist-dir-reason-tilde")
} else if value.contains('$') {
Some("cli-web-dist-dir-reason-dollar")
} else {
None
}
}
View on GitHub (pinned to 88bb9c8533)
Solutions
- Replace the value with a literal absolute path, e.g. `/home/user/zeroclaw/web/dist`.
- Expand the variable yourself before writing config: `zeroclaw.toml` written by a script should already contain the expanded result of `$HOME`.
- If the dashboard is not needed, remove `gateway.web_dist_dir` instead of leaving a broken path.
- Re-run `zeroclaw doctor` to confirm the warning is gone.
Example fix
# before [gateway] web_dist_dir = "~/zeroclaw/web/dist" # after [gateway] web_dist_dir = "/home/user/zeroclaw/web/dist"
Defensive patterns
Strategy: validation
Validate before calling
if let Some(dir) = config.gateway.web_dist_dir.as_deref() {
assert!(
!dir.starts_with('~') && !dir.contains('$'),
"gateway.web_dist_dir is read verbatim — expand it to a literal absolute path"
);
} Type guard
fn web_dist_dir_is_literal(path: &str) -> bool {
!path.starts_with('~') && !path.contains('$')
} Prevention
- Expand paths at config-generation time; never rely on the gateway to expand them.
- Prefer absolute paths for anything under [gateway].
When it happens
Trigger: Running `zeroclaw doctor` when `gateway.web_dist_dir` is set to a tilde path (`~/zeroclaw/web/dist`) or contains a dollar sign (`$HOME/zeroclaw/web/dist`, `${INSTALL_DIR}/web/dist`). Detection is `value.starts_with('~')` or `value.contains('$')`.
Common situations: Porting a shell-style path from documentation or dotfiles into zeroclaw.toml; CI templates that substitute variables at write time but leave the `$` syntax when substitution is disabled.
Related errors
- runtime.wasm.tools_dir cannot be empty
- model route "{}" uses invalid model_provider "{}": {}
- embedding route "{}" uses invalid model_provider "{}": {}
- agent "{name}" uses invalid model_provider "{provider_ref}":
- config section `{$path}` is malformed and was reset to defau
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/b0a74a8273691cf6.
Report an issue: GitHub.