← Back to PRs

#21164: feat(config): add lenient mode to resolveConfigEnvVars — preserve gateway-only env var refs without throwing

by Mellowambience open 2026-02-19 18:45 View on GitHub →
size: S
## Summary Fixes #21130. When a user places secrets in a systemd `EnvironmentFile=` drop-in for the gateway service and references them in `openclaw.json` via `${VAR}` syntax, **all CLI commands** (`openclaw doctor`, `openclaw gateway status`, etc.) fail with `MissingEnvVarError` — because the CLI process does not inherit the gateway's systemd environment. ## Root cause `resolveConfigEnvVars` (via `substituteString` in `env-substitution.ts`) unconditionally throws `MissingEnvVarError` for every unresolved `${VAR}` reference. There is no way to tell it "this var is gateway-only — leave it alone for read purposes." ## Fix Add an optional `{ lenient?: boolean }` parameter to `resolveConfigEnvVars`. When `lenient: true`: - Unresolvable `${VAR}` tokens are preserved as literal `${VAR}` strings instead of throwing. - Vars that **are** present in the environment are still substituted normally. - The default strict behaviour is completely unchanged — no existing call sites are affected. ## Changes ### `src/config/env-substitution.ts` - Add exported `ResolveEnvVarsOptions` type (`{ lenient?: boolean }`) - `substituteString` accepts options; preserves `${VAR}` literal when `lenient` and var is missing - `substituteAny` threads options through recursive calls - `resolveConfigEnvVars` signature updated: third arg `options: ResolveEnvVarsOptions = {}` ### `src/config/config.env-vars.test.ts` New `describe("lenient mode")` block — 4 tests: 1. `${VAR}` preserved as literal when var missing and `lenient: true` 2. Present vars still resolved normally in lenient mode 3. Default strict mode still throws `MissingEnvVarError` (regression guard) 4. Nested arrays/objects with multiple gateway-only refs all preserved ## Migration note No breaking changes. The `options` parameter is optional with a default of `{}`. Existing callers are unaffected. ## Diff summary ```diff export function resolveConfigEnvVars( obj: unknown, env: NodeJS.ProcessEnv = process.env, + options: ResolveEnvVarsOptions = {}, ): unknown { - return substituteAny(obj, env, ""); + return substituteAny(obj, env, "", options); } ``` <!-- greptile_comment --> <h3>Greptile Summary</h3> Added lenient mode to `resolveConfigEnvVars` that preserves unresolved `${VAR}` references as literals instead of throwing, solving CLI failures when config references gateway-only environment variables. **Key changes:** - New optional `lenient` parameter in `resolveConfigEnvVars` (backward compatible) - When enabled, missing env vars preserved as `${VAR}` strings rather than throwing `MissingEnvVarError` - Present vars still substituted normally in lenient mode - Comprehensive test coverage for lenient behavior **Issues found:** - Syntax error in test file: extra closing brace on line 63 breaks the test suite structure <h3>Confidence Score: 4/5</h3> - Safe to merge after fixing the syntax error in the test file - The implementation is solid and well-designed with proper backward compatibility, comprehensive tests, and clear documentation. The lenient mode logic correctly preserves unresolved vars while still substituting present ones. However, there's a critical syntax error (extra closing brace) that will prevent the test suite from running properly, which must be fixed before merge. - `src/config/config.env-vars.test.ts` has a syntax error that must be fixed <sub>Last reviewed commit: f1507a4</sub> <!-- greptile_other_comments_section --> <sub>(5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub> <!-- /greptile_comment -->

Most Similar PRs