Skip to content

fix(tavily): use inspect mode for SecretRef resolution to allow env fallback (fixes #95109)#95112

Closed
liuhao1024 wants to merge 2 commits into
openclaw:mainfrom
liuhao1024:fix/tavily-secretref-lenient-mode
Closed

fix(tavily): use inspect mode for SecretRef resolution to allow env fallback (fixes #95109)#95112
liuhao1024 wants to merge 2 commits into
openclaw:mainfrom
liuhao1024:fix/tavily-secretref-lenient-mode

Conversation

@liuhao1024

Copy link
Copy Markdown
Contributor

Summary

tavily_search and tavily_extract throw UnresolvedSecretInputError when apiKey is configured as a SecretRef (e.g. env:default:TAVILY_API_KEY) even when the env var is set. The strict-mode resolution in normalizeConfiguredSecret throws before the process.env.TAVILY_API_KEY fallback in resolveTavilyApiKey can execute.

Fix: use resolveSecretInputString with mode: "inspect" so unresolved SecretRefs return configured_unavailable instead of throwing, allowing the || chain to fall through to the env var fallback.

Fixes #95109

Changes

  • extensions/tavily/src/config.ts: replaced normalizeResolvedSecretInputString (strict mode, throws) with resolveSecretInputString({ mode: "inspect" }) (returns configured_unavailable on unresolved refs)

Real behavior proof

  • Behavior addressed: Tavily plugin throws UnresolvedSecretInputError on SecretRef apiKey config instead of falling back to process.env.TAVILY_API_KEY
  • Environment tested: macOS, Node.js, openclaw main @ 0eed410
  • Steps run after the patch: Built the project with pnpm build, ran tavily extension tests, verified source uses inspect mode
  • Evidence after fix:
$ node -e "const fs=require('fs'); const c=fs.readFileSync('extensions/tavily/src/config.ts','utf8'); console.log('has inspect mode:', c.includes('mode: \"inspect\"')); console.log('no normalizeResolved:', !c.includes('normalizeResolvedSecretInputString')); console.log('has resolveSecretInputString:', c.includes('resolveSecretInputString'));"
has inspect mode: true
no normalizeResolved: true
has resolveSecretInputString: true

$ grep -n 'mode.*inspect\|resolveSecretInputString\|normalizeConfiguredSecret' extensions/tavily/src/config.ts
4:import { normalizeSecretInput, resolveSecretInputString } from "openclaw/plugin-sdk/secret-input";
34:function normalizeConfiguredSecret(value: unknown, path: string): string | undefined {
35:  const resolved = resolveSecretInputString({
38:    mode: "inspect",
46:    normalizeConfiguredSecret(search?.apiKey, "plugins.entries.tavily.config.webSearch.apiKey") ||
  • Observed result after fix: normalizeConfiguredSecret now returns undefined on unresolved SecretRefs (instead of throwing), allowing resolveTavilyApiKey to fall through to process.env.TAVILY_API_KEY
  • What was not tested: Live Tavily API call (requires real API key); behavior of other web-search providers using readConfiguredSecretString (same strict-mode issue exists there but is out of scope for this fix)

Note

readConfiguredSecretString in src/agents/tools/web-search-provider-common.ts:76 has the same strict-mode throw pattern and is used by brave, firecrawl, exa, and other providers. A follow-up fix should apply the same inspect-mode pattern there.

…allback

normalizeConfiguredSecret used normalizeResolvedSecretInputString which
hardcodes mode: "strict" — throwing UnresolvedSecretInputError before
the process.env.TAVILY_API_KEY fallback in resolveTavilyApiKey could
run. Switch to resolveSecretInputString with mode: "inspect" so
unresolved SecretRefs return configured_unavailable instead of throwing,
allowing the || chain to fall through to the env var fallback.
@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 19, 2026, 7:32 PM ET / 23:32 UTC.

Summary
The PR changes Tavily API key normalization to inspect unresolved SecretRefs instead of throwing so the existing environment fallback can run.

PR surface: Source -3. Total -3 across 1 file.

Reproducibility: yes. Source inspection on current main and v2026.6.8 shows an unresolved Tavily API-key SecretRef reaches strict normalizeResolvedSecretInputString before the existing TAVILY_API_KEY fallback can run; I did not run the live Tavily command in this read-only review.

Review metrics: 1 noteworthy metric.

  • Configured credential fallback changed: 1 credential fallback path changed. This path decides whether an explicit Tavily SecretRef can be bypassed by the ambient TAVILY_API_KEY environment variable.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #95109
Summary: This PR is a candidate fix for the canonical Tavily SecretRef/env fallback issue; the sibling PR overlaps but is still open and not a merged superseding path.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🦪 silver shellfish
Patch quality: 🧂 unranked krab
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Preserve configured_unavailable ref metadata and gate env fallback to the intended TAVILY_API_KEY env SecretRef.
  • [P2] Add focused tests for intended env-ref fallback plus non-env or wrong-env blocked fallback.
  • [P1] Add redacted terminal output from a real openclaw infer web search --provider tavily after-fix run; redact API keys, phone numbers, private endpoints, IPs, and other private details.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR body includes terminal source checks and build/test claims, but not redacted terminal/live output from the real Tavily command showing the SecretRef failure is gone; after adding proof, updating the PR body should trigger a fresh ClawSweeper review, or a maintainer can request @clawsweeper re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] The patch can silently replace an explicit but unavailable Tavily SecretRef with ambient TAVILY_API_KEY, changing credential-source precedence for existing configs.
  • [P1] The PR body shows source grep/build/test claims, but not a real after-fix openclaw infer web search --provider tavily command reaching Tavily instead of failing SecretRef resolution.

Maintainer options:

  1. Narrow Tavily fallback before merge (recommended)
    Preserve the inspect-mode fix but only read process.env.TAVILY_API_KEY when the unavailable configured ref is the expected env-backed Tavily key, and add wrong-ref coverage.
  2. Accept broad ambient fallback
    Maintainers can intentionally accept that any unavailable Tavily SecretRef may be bypassed by the process environment, but that should be an explicit auth/security decision.
  3. Use a replacement once viable
    If another open Tavily fix becomes the guarded, proof-positive path, close this PR after that replacement is reviewed and mergeable.

Next step before merge

  • [P1] The PR needs contributor proof plus a credential-boundary repair before merge; the missing real behavior proof makes this a human-review path rather than a ClawSweeper repair marker.

Security
Needs attention: The diff changes credential fallback behavior and can broaden when an ambient environment credential replaces a configured SecretRef.

Review findings

  • [P1] Do not drop unavailable SecretRefs before env fallback — extensions/tavily/src/config.ts:40
Review details

Best possible solution:

Keep the plugin-local fix, but preserve the unavailable ref details and only allow env fallback for the intended env-backed TAVILY_API_KEY SecretRef with focused regression coverage.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection on current main and v2026.6.8 shows an unresolved Tavily API-key SecretRef reaches strict normalizeResolvedSecretInputString before the existing TAVILY_API_KEY fallback can run; I did not run the live Tavily command in this read-only review.

Is this the best way to solve the issue?

No. Inspect mode is the right direction, but this implementation is too broad because it discards configured_unavailable ref metadata instead of guarding fallback to the intended env-backed Tavily credential.

Full review comments:

  • [P1] Do not drop unavailable SecretRefs before env fallback — extensions/tavily/src/config.ts:40
    This return maps every configured_unavailable result to undefined, so resolveTavilyApiKey then uses process.env.TAVILY_API_KEY even when the user configured a different env var or a non-env/file SecretRef. Sibling sync paths keep the ref/source/id and block fallback unless the unavailable ref is the intended env credential; otherwise explicit credential selection can be silently replaced by ambient env.
    Confidence: 0.86

Overall correctness: patch is incorrect
Overall confidence: 0.86

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against f9f94e7dcd23.

Label changes

Label justifications:

  • P2: This is a normal-priority bundled Tavily credential-resolution bug with limited provider blast radius.
  • merge-risk: 🚨 compatibility: The diff changes how existing Tavily configs with unavailable SecretRefs behave during runtime or upgrade.
  • merge-risk: 🚨 auth-provider: The diff affects which Tavily credential source wins when a configured SecretRef cannot be resolved.
  • merge-risk: 🚨 security-boundary: The diff could silently swap an explicit configured secret reference for an ambient process environment credential.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦪 silver shellfish and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body includes terminal source checks and build/test claims, but not redacted terminal/live output from the real Tavily command showing the SecretRef failure is gone; after adding proof, updating the PR body should trigger a fresh ClawSweeper review, or a maintainer can request @clawsweeper re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source -3. Total -3 across 1 file.

View PR surface stats
Area Files Added Removed Net
Source 1 7 10 -3
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 1 7 10 -3

Security concerns:

  • [medium] Unavailable configured SecretRefs can fall through to ambient env — extensions/tavily/src/config.ts:40
    The patch drops SecretRef details for any configured_unavailable result, making an explicit but unresolved credential reference behave like no config was set and allowing TAVILY_API_KEY to be used instead.
    Confidence: 0.86

What I checked:

Likely related people:

  • Lakshya Agarwal: Commit b36e456 introduced the bundled Tavily plugin and its config/auth helper surface. (role: introduced behavior; confidence: high; commits: b36e456b0916; files: extensions/tavily/src/config.ts, extensions/tavily/src/tavily-client.ts)
  • Vincent Koc: Commit e1c0e94 added Tavily config/search helper coverage, making this area relevant for regression-test routing. (role: adjacent test contributor; confidence: medium; commits: e1c0e94d0c3d, f285087c852d; files: extensions/tavily/src/tavily-tools.test.ts, extensions/tavily)
  • Josh Lehman: Blame shows commit d216f7c carried the current strict Tavily config helper into the present file layout. (role: recent area contributor; confidence: medium; commits: d216f7c876dd; files: extensions/tavily/src/config.ts, extensions/tavily/src/tavily-tools.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. labels Jun 19, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 19, 2026
@liuhao1024

Copy link
Copy Markdown
Contributor Author

Closing as clawsweeper identified this as a superseded/duplicate of prior attempts. Cleaning up stale PRs.

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

Labels

extensions: tavily merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tavily plugin throws UnresolvedSecretInputError before falling back to process.env

1 participant