fix: Handle SecretRef in getCustomProviderApiKey#49805
Conversation
Handle environment variable references (SecretRef) properly in getCustomProviderApiKey function. Previously, when the apiKey was configured as a SecretRef pointing to an environment variable, it wasn't being resolved correctly. Changes: - Import resolveSecretInputRef from config/types.secrets.js - Check if apiKey is a SecretRef - Resolve environment variable references properly - Return undefined for unresolved secrets Fixes openclaw#49736 Generated with OpenClaw Agent
Greptile SummaryThis PR fixes a real bug where Key changes:
Issues found:
Confidence Score: 4/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/agents/model-auth.ts
Line: 6
Comment:
**Unused import: `coerceSecretRef`**
`coerceSecretRef` is imported here but never referenced in the file. The resolution logic already goes through `resolveSecretInputRef` (which calls `coerceSecretRef` internally), so this import is redundant.
```suggestion
import { resolveSecretInputRef } from "../config/types.secrets.js";
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: src/agents/model-auth.ts
Line: 64-70
Comment:
**SecretRef provider field and allowlist not respected**
The resolution short-circuits to `process.env[ref.id]` for any `env` source, but `SecretsConfig` supports multiple named env providers (via `cfg?.secrets?.providers`), each of which can carry an `allowlist` of permitted env var names. By ignoring `ref.provider`, this code will happily read an env var that a non-default provider would restrict.
For the common `provider: "default"` case this is fine, but if a user has configured a custom env provider with an allowlist, the allowlist is silently bypassed. At a minimum, consider logging a warning or gating the direct `process.env` read to `ref.provider === DEFAULT_SECRET_PROVIDER_ALIAS` only:
```typescript
if (ref.source === "env" && ref.provider === DEFAULT_SECRET_PROVIDER_ALIAS) {
const envValue = process.env[ref.id];
if (envValue) {
return normalizeOptionalSecretInput(envValue);
}
}
```
This keeps the fix correct for the reported bug while making it clear that non-default providers fall through to the `return undefined` path (with a follow-up comment explaining the gap).
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: "fix: Handle SecretRe..." |
| import { formatCliCommand } from "../cli/command-format.js"; | ||
| import type { OpenClawConfig } from "../config/config.js"; | ||
| import type { ModelProviderAuthMode, ModelProviderConfig } from "../config/types.js"; | ||
| import { coerceSecretRef, resolveSecretInputRef } from "../config/types.secrets.js"; |
There was a problem hiding this comment.
Unused import:
coerceSecretRef
coerceSecretRef is imported here but never referenced in the file. The resolution logic already goes through resolveSecretInputRef (which calls coerceSecretRef internally), so this import is redundant.
| import { coerceSecretRef, resolveSecretInputRef } from "../config/types.secrets.js"; | |
| import { resolveSecretInputRef } from "../config/types.secrets.js"; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/model-auth.ts
Line: 6
Comment:
**Unused import: `coerceSecretRef`**
`coerceSecretRef` is imported here but never referenced in the file. The resolution logic already goes through `resolveSecretInputRef` (which calls `coerceSecretRef` internally), so this import is redundant.
```suggestion
import { resolveSecretInputRef } from "../config/types.secrets.js";
```
How can I resolve this? If you propose a fix, please make it concise.| if (ref) { | ||
| if (ref.source === "env") { | ||
| const envValue = process.env[ref.id]; | ||
| if (envValue) { | ||
| return normalizeOptionalSecretInput(envValue); | ||
| } | ||
| } |
There was a problem hiding this comment.
SecretRef provider field and allowlist not respected
The resolution short-circuits to process.env[ref.id] for any env source, but SecretsConfig supports multiple named env providers (via cfg?.secrets?.providers), each of which can carry an allowlist of permitted env var names. By ignoring ref.provider, this code will happily read an env var that a non-default provider would restrict.
For the common provider: "default" case this is fine, but if a user has configured a custom env provider with an allowlist, the allowlist is silently bypassed. At a minimum, consider logging a warning or gating the direct process.env read to ref.provider === DEFAULT_SECRET_PROVIDER_ALIAS only:
if (ref.source === "env" && ref.provider === DEFAULT_SECRET_PROVIDER_ALIAS) {
const envValue = process.env[ref.id];
if (envValue) {
return normalizeOptionalSecretInput(envValue);
}
}This keeps the fix correct for the reported bug while making it clear that non-default providers fall through to the return undefined path (with a follow-up comment explaining the gap).
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/model-auth.ts
Line: 64-70
Comment:
**SecretRef provider field and allowlist not respected**
The resolution short-circuits to `process.env[ref.id]` for any `env` source, but `SecretsConfig` supports multiple named env providers (via `cfg?.secrets?.providers`), each of which can carry an `allowlist` of permitted env var names. By ignoring `ref.provider`, this code will happily read an env var that a non-default provider would restrict.
For the common `provider: "default"` case this is fine, but if a user has configured a custom env provider with an allowlist, the allowlist is silently bypassed. At a minimum, consider logging a warning or gating the direct `process.env` read to `ref.provider === DEFAULT_SECRET_PROVIDER_ALIAS` only:
```typescript
if (ref.source === "env" && ref.provider === DEFAULT_SECRET_PROVIDER_ALIAS) {
const envValue = process.env[ref.id];
if (envValue) {
return normalizeOptionalSecretInput(envValue);
}
}
```
This keeps the fix correct for the reported bug while making it clear that non-default providers fall through to the `return undefined` path (with a follow-up comment explaining the gap).
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 13195ffdd5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const { ref } = resolveSecretInputRef({ value: entry.apiKey }); | ||
| if (ref) { | ||
| if (ref.source === "env") { | ||
| const envValue = process.env[ref.id]; | ||
| if (envValue) { |
There was a problem hiding this comment.
Respect SecretRef provider validation for custom API keys
When models.providers.<id>.apiKey is an env SecretRef, this branch reads process.env[ref.id] directly and never goes through the normal resolver. That skips the provider/source checks in src/secrets/resolve.ts:184-203 and the env-provider allowlist enforcement in src/secrets/resolve.ts:350-369, so resolveApiKeyForProvider() can now accept a variable that the configured secret provider would reject (for example a typoed provider alias, a source mismatch, or an env name outside secrets.providers.<alias>.allowlist). In those setups the agent will silently use credentials that the config declared invalid instead of surfacing the unresolved-secret error.
Useful? React with 👍 / 👎.
|
Thanks for the contribution here, @xi7ang. Outcome: this PR is superseded by #66818, which landed the fix as part of the broader SecretRef lifecycle alignment. Why #66818 covers this: it implements custom provider SecretRef handling and extends it with provider-policy checks (env source + allowlist) so read-only resolution stays safe and consistent. |
Problem
Agent fails with error: "Agent failed before reply: No API key found for provider 'custom-192-168-0-222-11434'"
Root Cause
When
apiKeyin provider config is a SecretRef (e.g.,{ source: "env", provider: "default", id: "API_KEY" }), thegetCustomProviderApiKeyfunction doesn't resolve the environment variable reference properly.Solution
Modified
getCustomProviderApiKeyto:resolveSecretInputRefChanges
src/agents/model-auth.ts: Added SecretRef handling ingetCustomProviderApiKeyTesting
Tested with custom provider configured with environment variable API key reference.
Fixes #49736