Skip to content

fix(providers): wrap provider apiKey env ref in ${} so OpenClaw expands it#1134

Open
dongsheng123132 wants to merge 1 commit into
ValueCell-ai:mainfrom
dongsheng123132:fix/provider-apikey-env-ref
Open

fix(providers): wrap provider apiKey env ref in ${} so OpenClaw expands it#1134
dongsheng123132 wants to merge 1 commit into
ValueCell-ai:mainfrom
dongsheng123132:fix/provider-apikey-env-ref

Conversation

@dongsheng123132

Copy link
Copy Markdown

Problem

App-internal chat returns 401 Invalid token for every env-keyed (non-OAuth) provider — openai, deepseek, openrouter, minimax, etc.

OpenClaw's 2026.6.x runtime removed the apiKeyEnv config field and now expands ${ENV_VAR} placeholders inside config string values instead (docs/help/environment.md, e.g. apiKey: "${VERCEL_GATEWAY_API_KEY}").

But upsertOpenClawProviderEntry still writes the bare env name into provider.apiKey:

if (options.apiKeyEnv) nextProvider.apiKey = options.apiKeyEnv; // -> "OPENAI_API_KEY"

So OpenClaw sends the literal string OPENAI_API_KEY as 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

if (options.apiKeyEnv) nextProvider.apiKey = '${' + options.apiKeyEnv + '}';

Verification

Packaged + launched the app:

  • openclaw.json now writes apiKey: "${OPENAI_API_KEY}"
  • gateway prewarm provider-auth-sync / models.authStatus / models.list all OK (previously 401)
  • real chat.send -> model streamed a reply, zero 401

No test changes needed: the existing *_API_KEY strings in the suite are writeOpenClawJson inputs, not assertions on the upsert output.

…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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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 + '}';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant