Skip to content

fix(delegate): inherit parent api_key when delegation.base_url set without delegation.api_key (#15810)#15853

Closed
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/delegation-base-url-credential-chain-15810
Closed

fix(delegate): inherit parent api_key when delegation.base_url set without delegation.api_key (#15810)#15853
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/delegation-base-url-credential-chain-15810

Conversation

@briandevans

Copy link
Copy Markdown
Contributor

Summary

  • When delegation.base_url is configured without delegation.api_key, the child agent now inherits the parent's resolved API key instead of falling back to OPENAI_API_KEY
  • Explicit delegation.api_key still overrides as before
  • Two tests updated to reflect the corrected behaviour

The bug

_resolve_delegation_credentials() handled the delegation.base_url path as:

api_key = configured_api_key or os.getenv("OPENAI_API_KEY", "").strip()
if not api_key:
    raise ValueError("... Set delegation.api_key or OPENAI_API_KEY.")

This bypassed the provider credential pool entirely. Providers that store their key outside OPENAI_API_KEY (e.g. MINIMAX_API_KEY, DASHSCOPE_API_KEY) therefore caused the child agent to receive an empty API key → 401 Unauthorized. By contrast, the delegation.provider-only path correctly resolved credentials via resolve_runtime_provider().

The fix

When delegation.api_key is absent, return api_key = None instead of the OPENAI_API_KEY fallback. _spawn_child already has the correct inheritance logic:

effective_api_key = override_api_key or parent_api_key

With override_api_key = None, this naturally falls through to parent_api_key, which holds the key the parent agent resolved through the full provider credential chain.

Test plan

  • Before: test_direct_endpoint_returns_none_api_key_when_not_configured and test_direct_endpoint_no_raise_when_only_provider_env_key_present — both FAIL on unpatched code (one gets wrong "env-openai-key", one expects no ValueError but gets one)
  • After: 115/115 tests pass in tests/tools/test_delegate.py
  • Regression guard: reverted production change → both new tests fail; restored → 0 failures
  • Adjacent toolset tests unchanged

Related

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings April 26, 2026 02:13

Copilot AI 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.

Pull request overview

This PR fixes delegation credential inheritance when delegation.base_url is set without delegation.api_key, ensuring subagents inherit the parent agent’s resolved API key (via the provider credential chain) instead of falling back to OPENAI_API_KEY.

Changes:

  • Update _resolve_delegation_credentials() so the base_url branch returns api_key=None when not explicitly configured, enabling inheritance in child construction.
  • Expand/update delegation credential resolution docstring/comments to describe the intended inheritance behavior.
  • Update two delegation credential resolution tests to assert api_key is None (no OPENAI_API_KEY fallback and no ValueError).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
tools/delegate_tool.py Adjusts delegation credential resolution so direct-endpoint delegation inherits the parent API key when delegation.api_key is omitted.
tests/tools/test_delegate.py Updates tests to reflect the new inheritance behavior when base_url is configured without api_key.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tools/delegate_tool.py Outdated
Comment on lines +2180 to +2182
omitted, ``api_key`` is returned as None so ``_spawn_child`` inherits the
parent agent's key (``effective_api_key = override_api_key or parent_api_key``).
This lets providers that store their key outside ``OPENAI_API_KEY`` (e.g.

Copilot AI Apr 26, 2026

Copy link

Choose a reason for hiding this comment

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

The docstring references _spawn_child, but there is no _spawn_child function in this module/repo. The credential inheritance logic (effective_api_key = override_api_key or parent_api_key) lives in _build_child_agent, so these references should be updated to avoid misleading future readers.

Suggested change
omitted, ``api_key`` is returned as None so ``_spawn_child`` inherits the
parent agent's key (``effective_api_key = override_api_key or parent_api_key``).
This lets providers that store their key outside ``OPENAI_API_KEY`` (e.g.
omitted, ``api_key`` is returned as None so the child-building path keeps
inheriting the parent agent's key in ``_build_child_agent``
(``effective_api_key = override_api_key or parent_api_key``). This lets
providers that store their key outside ``OPENAI_API_KEY`` (e.g.

Copilot uses AI. Check for mistakes.
Comment thread tools/delegate_tool.py Outdated
Comment on lines +2200 to +2206
# When delegation.api_key is not set, return None so _spawn_child falls
# back to the parent agent's API key via the credential inheritance path
# (effective_api_key = override_api_key or parent_api_key). This lets
# providers that store their key in a non-OPENAI_API_KEY env var (e.g.
# MINIMAX_API_KEY, DASHSCOPE_API_KEY) work without requiring callers to
# duplicate the key under delegation.api_key.
api_key = configured_api_key # None → inherited from parent in _spawn_child

Copilot AI Apr 26, 2026

Copy link

Choose a reason for hiding this comment

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

These inline comments mention _spawn_child, but the actual inheritance logic occurs in _build_child_agent (where effective_api_key is computed). Please rename the referenced function in the comment so it matches the code path and is searchable.

Copilot uses AI. Check for mistakes.
Comment thread tests/tools/test_delegate.py Outdated
def test_direct_endpoint_falls_back_to_openai_api_key_env(self):
def test_direct_endpoint_returns_none_api_key_when_not_configured(self):
# When base_url is set without api_key, api_key should be None so
# _spawn_child inherits the parent's key (effective_api_key = override or parent).

Copilot AI Apr 26, 2026

Copy link

Choose a reason for hiding this comment

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

This test comment refers to _spawn_child, but delegation constructs children via _build_child_agent (which computes effective_api_key = override_api_key or parent_api_key). Updating the comment will keep the test documentation accurate and easier to follow.

Suggested change
# _spawn_child inherits the parent's key (effective_api_key = override or parent).
# _build_child_agent falls back to the parent's key
# (effective_api_key = override_api_key or parent_api_key).

Copilot uses AI. Check for mistakes.
Comment thread tests/tools/test_delegate.py Outdated
Comment on lines 661 to 662
# Even if OPENAI_API_KEY is absent, no ValueError — parent key is the fallback.
parent = _make_mock_parent(depth=0)

Copilot AI Apr 26, 2026

Copy link

Choose a reason for hiding this comment

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

This test comment mentions _spawn_child, but the inheritance logic is implemented in _build_child_agent. Consider updating the wording so readers can find the relevant code quickly.

Copilot uses AI. Check for mistakes.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tools Tool registry, model_tools, toolsets tool/delegate Subagent delegation labels Apr 26, 2026
@briandevans

Copy link
Copy Markdown
Contributor Author

Thanks @copilot — all four findings are the same root cause: I wrote _spawn_child but the actual function is _build_child_agent. Addressed in c6a16eda.

All four comments → renamed every occurrence of _spawn_child to _build_child_agent in:

  • tools/delegate_tool.py docstring (line 2182)
  • tools/delegate_tool.py inline comment (line 2206)
  • tests/tools/test_delegate.py test comment (line 649)
  • tests/tools/test_delegate.py test comment (line 662)

@briandevans

Copy link
Copy Markdown
Contributor Author

@copilot All four _spawn_child_build_child_agent comment corrections are done in commit c6a16eda. The docstring, inline comments in delegate_tool.py, and test comments in test_delegate.py now all reference _build_child_agent correctly.

briandevans and others added 2 commits May 3, 2026 20:12
…thout api_key (NousResearch#15810)

When delegation.base_url is configured without delegation.api_key, the
old code fell back to os.getenv("OPENAI_API_KEY"), bypassing the
provider credential pool.  Providers that store their key outside
OPENAI_API_KEY (e.g. MINIMAX_API_KEY, DASHSCOPE_API_KEY) therefore
received an empty key → 401 Unauthorized.

Return None for api_key when delegation.api_key is absent so that
_spawn_child picks up the parent agent's resolved key via the existing
inheritance path (effective_api_key = override_api_key or parent_api_key).
Explicit delegation.api_key still takes effect as before.

Update two tests that asserted the old OPENAI_API_KEY fallback behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@teknium1

teknium1 commented May 4, 2026

Copy link
Copy Markdown
Contributor

Salvaged via #19741 onto current main. Docstring conflict resolved by merging both code paths' documentation; two existing tests that locked in the old OPENAI_API_KEY-fallback behavior were updated to expect the new parent-inheritance semantics. Your authorship was preserved. Thanks @briandevans!

@teknium1 teknium1 closed this May 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists tool/delegate Subagent delegation type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] delegate_task: api_key falls back to OPENAI_API_KEY instead of provider credential chain when delegation.base_url is set

4 participants