Ask: Fall back to the parent model when a sub-agent's model fails at call-time
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 52/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- rust, typescript
- Domain
- ai-infra-agents, backend-api-design
Research direction
Start with custom_model.rs::resolve_custom_agent_model and agent_tools.rs::validate_and_resolve_subagent_model, then trace the call-time error path through sessionExecutor.ts and session.ts. Reproduce the unavailable-model 400 described in the issue and inspect the ModelCallFailure data in session-events.d.ts. Done means a sub-agent retries once with the parent model after an unavailable-model failure and the turn continues.
Written by the indexing model from the issue text.
Description
Component: @github/copilot-sdk + copilot-agent-runtime — sub-agent model resolution
Status: Behavior does not match the documented CustomAgentConfig.model contract
Model IDs below are redacted placeholders:
<unavailable-model>= a model in the catalog but not onboarded / no-capacity for the test user;<parent-model>= the (available) parent session model.
1. TL;DR
Ask. When a sub-agent's configured model fails at call-time — i.e. when the runtime issues the actual model request to CAPI (the Copilot model API) — with a model-unavailable 400 (unknown / not onboarded / no capacity), the runtime should fall back to the parent session model and retry that sub-agent's call — instead of letting the sub-agent's model call fail.
Why this is the contract, not a feature request. The SDK already documents this fallback: CustomAgentConfig.model "falling back to the parent session model if unavailable" (types.d.ts:1160-1165). We observe it does not happen at call-time.
One-line evidence. A sub-agent set to <unavailable-model> under a <parent-model> parent → the model is sent to CAPI unchanged → CAPI returns 400 Unknown model "<unavailable-model>" → no retry with the parent model → the sub-agent's call fails.
2. The documented contract
A host application embeds @github/copilot-sdk and dispatches sub-agents via SessionConfig.customAgents: CustomAgentConfig[]. Each sub-agent's model is a string on CustomAgentConfig.model, documented as:
// @github/copilot-sdk/dist/types.d.ts:1160-1165
/**
* Model identifier for this agent (e.g. "claude-haiku-4.5").
* When set, the runtime will attempt to use this model for the agent,
* falling back to the parent session model if unavailable.
*/
model?: string;
Three cases follow; we rely on the third:
model value |
Documented behavior |
|---|---|
| set to a valid model | used for the sub-agent |
omitted / undefined |
sub-agent inherits the parent session model |
| set but unavailable | fall back to the parent session model and continue |
The third row is what fails at call-time.
3. The gap (observed)
For a sub-agent model that is unavailable through the user's CAPI endpoint — e.g. not onboarded or no capacity (<unavailable-model> in our repro) — we observe in the outgoing request and in the event stream:
- The configured model is sent to CAPI unchanged (logged: the sub-agent's first request carries
<unavailable-model>). - CAPI returns
400:Unknown model "<unavailable-model>". Please ensure the model is onboarded. - The runtime emits an ephemeral, telemetry-only
model.call_failure(source=subagent,statusCode=400,model="<unavailable-model>"). - No fallback: no call is re-issued with the parent model. The sub-agent's model call fails.
Confirmed gap: the documented "fall back to the parent session model if unavailable" is not honored for a call-time
400. (Our source-level reading of why is in Appendix §6.3 — hypothesis only.)
4. Evidence
4.1 Setup
- A host embedding
@github/copilot-sdk; a local end-to-end run. - Parent session model:
<parent-model>. - Three sub-agents dispatched via
SessionConfig.customAgents:- A, C →
<unavailable-model>(unavailable → 400) - B →
<parent-model>(available → success)
- A, C →
- All sub-agent event hooks,
model.call_failure, and the model sent on each request are instrumented.
4.2 Event sequence
subagent.started×3 (A, B, C).- First model request per sub-agent: A→
<unavailable-model>, B→<parent-model>, C→<unavailable-model>. model.call_failure×2 (A, C) — ephemeral / telemetry-only:
({ "source": "subagent", "initiator": "sub-agent", "statusCode": 400, "model": "<unavailable-model>", "errorMessage": "...Unknown model \"<unavailable-model>\". Please ensure the model is onboarded.", "agentId": "call_xxx" }agentIdis the Task tool-call ID, not the sub-agent name.)- No parent-model retry follows. The sub-agent's call fails.
4.3 Downstream behavior after the 400 — not confirmed
Our probe covered only background/detached dispatch. We did not verify what happens after the 400 on the normal foreground path, so we make no claim about whether the failure stays isolated to the sub-agent or affects the parent session. Appendix §6.3 records a source-level hypothesis only.
4.4 Why the host cannot recover this itself
model.call_failureisephemeraltelemetry (session-events.d.ts:2821-2887,ephemeral: true); we found no in-turn hook that lets the host intercept the400and retry the sub-agent with another model within the same turn.- Per the SDK types,
customAgentsis applied when a session is created or resumed (types.d.ts—SessionConfigBase.customAgents, inherited byResumeSessionConfig). The host therefore cannot change the model during the failing turn — only after resuming the session, which is too late to recover the first turn. So the fallback must occur inside the runtime.
5. What we're asking
1. Honor the documented fallback at call-time.
When CAPI rejects a sub-agent's CustomAgentConfig.model as unavailable (400), retry that sub-agent's call once with the parent session model, then continue the turn — matching the model? contract.
The runtime can identify this condition from the failure data already present: statusCode plus the provider error / structured badRequestKind (session-events.d.ts:2821-2887). A one-shot fallback scoped to the sub-agent is sufficient.
6. Appendix
6.1 Minimal repro
- Configure a sub-agent with
CustomAgentConfig.model= a model that is in the catalog but not onboarded / no-capacity for the test user (<unavailable-model>; parent<parent-model>). - Dispatch it.
- Observe
model.call_failure (400)with no parent-model retry; the sub-agent's call fails.
6.2 Key references
@github/copilot-sdk/dist/types.d.ts:1160-1165—CustomAgentConfig.modelfallback contract.@github/copilot-sdk/dist/generated/session-events.d.ts:2821-2887—ModelCallFailureEvent/ModelCallFailureData(ephemeral: true,initiator="sub-agent",statusCode,model,errorMessage).- Observed call-time
400:Unknown model "<unavailable-model>". Please ensure the model is onboarded.
6.3 Our runtime-source reading (hypothesis — please verify)
Disclaimer: this is our interpretation of
copilot-agent-runtime, offered only as a starting pointer. We may be reading the wrong path. Please verify. The ask in §5 stands on the observed behavior (§3 / §4) regardless.
On why no fallback fires (§3). Sub-agent model selection appears to run through one of two functions, and both seem to fail open (no fallback) when the host supplies no populated available_models list:
custom_model.rs::resolve_custom_agent_model— emptyavailable_models→ first candidate returned verbatim ("CAPI will validate"), no check, no fallback (:236-260); non-empty + unresolved → falls back to session model (:317-328, the only fallback path we see).agent_tools.rs::validate_and_resolve_subagent_model— empty → model verbatim (:171-173); non-empty + unresolvable →Error, no fallback (:182-190).available_modelsis populated from the CAPI session bootstrap (model/session.rs:19,96;model/capi_client.rs:1587-1628); an embedded host appears not to feed a catalog, which would leave the list empty on our client.- Net (our reading): with an empty list the configured model goes straight to CAPI with no check and no fallback →
400. A populated list may still not fix it — membership is not a capacity check, so an in-list but no-capacity model would still400at call-time. This is why we ask for a call-time fallback (§5).
On the foreground path (§4.3). The source suggests the normal foreground path degrades gracefully: SessionAgentExecutor.run() catches a sub-agent turn error and emits subagent.failed, returning the failure to the parent as a tool result so the turn continues (sessionExecutor.ts:282-286; session.ts:12364-12382). We have not verified this end-to-end.
- Dominant language
- Java
- Stars
- 10.5k
- Forks
- 1.5k
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 133
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from github/copilot-sdk
-
agentic-workflows
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
github/copilot-sdk#2709 · 1 comment ·
-
Difficulty 1/5 Under an hour Newbie friendliness 78/100
github/copilot-sdk#2673 ·
-
bug testing
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
github/copilot-sdk#2628 ·
-
agentic-workflows
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
github/copilot-sdk#2627 · 1 comment ·
-
agentic-workflows
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
github/copilot-sdk#2493 ·
All issues in github/copilot-sdk
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
checkstyle/test-configs#263 ·
-
bug
Difficulty 1/5 Under an hour Newbie friendliness 90/100
apache/cloudstack#14222 ·
-
[BUG]茶杯方块在取茶时会引发崩溃 Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
1.0.0-alpha2 Type/Improvement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
wso2/dpdp-accelerator#272 ·