fix(providers): wrap provider apiKey env ref in ${} so OpenClaw expands it#1134
fix(providers): wrap provider apiKey env ref in ${} so OpenClaw expands it#1134dongsheng123132 wants to merge 1 commit into
Conversation
…ds it
OpenClaw's 2026.6.x runtime removed the `apiKeyEnv` config field and now
expands `${ENV_VAR}` placeholders inside config string values instead (see
docs/help/environment.md, e.g. `apiKey: "${VERCEL_GATEWAY_API_KEY}"`).
upsertOpenClawProviderEntry still wrote the bare env name (e.g.
`"OPENAI_API_KEY"`) into `provider.apiKey`, so OpenClaw sent that literal
string as the bearer token. Every model call to an env-keyed provider then
returned `401 Invalid token`, while the real key the gateway launcher injects
into the environment went unused. curl with the real key worked, masking it.
Wrap the value in `${...}` so OpenClaw substitutes the env value. Affects all
env-keyed (non-OAuth) providers; app-internal chat was fully broken for them.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 19c1866f74
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // value must be wrapped in `${...}`; a bare env name is treated as a literal | ||
| // string and sent verbatim as the bearer token, so every model call to that | ||
| // provider fails with 401 Invalid token while the real key sits unused in env. | ||
| if (options.apiKeyEnv) nextProvider.apiKey = '${' + options.apiKeyEnv + '}'; |
There was a problem hiding this comment.
Don't wrap OAuth sentinel api keys as env placeholders
This helper is also used by the MiniMax OAuth paths, which pass apiKeyEnv: 'minimax-oauth' (see device-oauth.ts and the provider switch flow) to tell the Gateway to read the OAuth profile rather than an environment variable. Wrapping every value here changes that required sentinel into ${minimax-oauth}, so users who select the default MiniMax OAuth/device auth path will get a provider config that no longer points at their stored auth-profiles.json token and model calls fail auth. Please only wrap real environment variable names, or add a separate option for OAuth sentinels.
Useful? React with 👍 / 👎.
Problem
App-internal chat returns
401 Invalid tokenfor every env-keyed (non-OAuth) provider — openai, deepseek, openrouter, minimax, etc.OpenClaw's 2026.6.x runtime removed the
apiKeyEnvconfig field and now expands${ENV_VAR}placeholders inside config string values instead (docs/help/environment.md, e.g.apiKey: "${VERCEL_GATEWAY_API_KEY}").But
upsertOpenClawProviderEntrystill writes the bare env name intoprovider.apiKey:So OpenClaw sends the literal string
OPENAI_API_KEYas the bearer token and 401s, while the real key — correctly injected into the gateway process env by the launcher — is never used. This is easy to miss:curl-ing the provider with the real key succeeds; only the in-app path (which relies on${}substitution) breaks.Fix
Verification
Packaged + launched the app:
openclaw.jsonnow writesapiKey: "${OPENAI_API_KEY}"provider-auth-sync/models.authStatus/models.listall OK (previously 401)chat.send-> model streamed a reply, zero 401No test changes needed: the existing
*_API_KEYstrings in the suite arewriteOpenClawJsoninputs, not assertions on the upsert output.