Skip to content

fix: Handle SecretRef in getCustomProviderApiKey#49805

Closed
xi7ang wants to merge 1 commit into
openclaw:mainfrom
xi7ang:fix/issue-49736
Closed

fix: Handle SecretRef in getCustomProviderApiKey#49805
xi7ang wants to merge 1 commit into
openclaw:mainfrom
xi7ang:fix/issue-49736

Conversation

@xi7ang

@xi7ang xi7ang commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Problem

Agent fails with error: "Agent failed before reply: No API key found for provider 'custom-192-168-0-222-11434'"

Root Cause

When apiKey in provider config is a SecretRef (e.g., { source: "env", provider: "default", id: "API_KEY" }), the getCustomProviderApiKey function doesn't resolve the environment variable reference properly.

Solution

Modified getCustomProviderApiKey to:

  1. Check if apiKey is a SecretRef using resolveSecretInputRef
  2. Resolve environment variable references when source is "env"
  3. Handle unresolved secrets gracefully by returning undefined

Changes

  • src/agents/model-auth.ts: Added SecretRef handling in getCustomProviderApiKey

Testing

Tested with custom provider configured with environment variable API key reference.


Fixes #49736

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
@xi7ang xi7ang requested a review from a team as a code owner March 18, 2026 12:55
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Mar 18, 2026
@greptile-apps

greptile-apps Bot commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a real bug where getCustomProviderApiKey would silently return undefined when apiKey in the provider config was a SecretRef object (or an ${ENV_VAR} template string) instead of a plain string, causing agents to fail with "No API key found for provider". The fix correctly uses resolveSecretInputRef to detect and unwrap env-based SecretRefs before falling back to the existing plain-string path.

Key changes:

  • src/agents/model-auth.ts: getCustomProviderApiKey now detects SecretRef inputs via resolveSecretInputRef, resolves env sources from process.env, and returns undefined for unresolvable file/exec sources.

Issues found:

  • coerceSecretRef is imported but never referenced in the modified file — the unused import should be removed.
  • The ref.provider field is ignored when reading process.env[ref.id]; users who configure a non-default env provider with an allowlist will have that allowlist silently bypassed. Scoping the direct process.env read to DEFAULT_SECRET_PROVIDER_ALIAS only would make the behaviour more predictable.

Confidence Score: 4/5

  • Safe to merge with a trivial fix for the unused import; the core logic is correct for the common case.
  • The change is small and well-scoped, and the bug it addresses is clearly real. The logic for detecting and resolving env SecretRefs is sound. The two concerns (unused import and provider allowlist bypass) are low-severity — the unused import is a lint issue and the allowlist concern is an edge case that doesn't affect standard configurations. No regressions are introduced for the existing plain-string path.
  • src/agents/model-auth.ts — remove the unused coerceSecretRef import.
Prompt To Fix All 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.

---

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

Comment thread src/agents/model-auth.ts
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";

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.

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

Suggested change
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.

Comment thread src/agents/model-auth.ts
Comment on lines +64 to +70
if (ref) {
if (ref.source === "env") {
const envValue = process.env[ref.id];
if (envValue) {
return normalizeOptionalSecretInput(envValue);
}
}

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.

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

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

Copy link
Copy Markdown

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: 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".

Comment thread src/agents/model-auth.ts
Comment on lines +63 to +67
const { ref } = resolveSecretInputRef({ value: entry.apiKey });
if (ref) {
if (ref.source === "env") {
const envValue = process.env[ref.id];
if (envValue) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

@joshavant

Copy link
Copy Markdown
Contributor

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.

@joshavant joshavant closed this Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Agent failed before reply: No API key found for provider "custom-192-168-0-222-11434".

2 participants