Unknown model ID silently falls back to another model without an error or model-change event
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 4/5
- Tempo estimado
- 3-5 dias
- Facilidade para iniciantes
- 48/100
- Tipo de issue
- Bug
- Clareza
- Claramente especificada
- Status de atividade
- Ativa
- Stack de tecnologia
- node.js, typescript
- Domínio
- backend-api-design
Direção de pesquisa
Start by reading createSession() model selection and validation, then trace when the effective model is resolved and how session.warning and session.model_change events are emitted. Check existing model fallback tests and the related discussion in issue #250. Done means an unavailable explicitly requested model can be rejected or its fallback is observable, with behavior covered by tests and matching documentation.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
An explicitly requested, nonexistent model ID is accepted by createSession(). The first turn then runs on a different model without an exception, session.warning, or session.model_change event.
This prevents an SDK caller from relying on an explicit model selection. Checking session.rpc.model.getCurrent() immediately after creation does not detect the problem: it initially reports the invalid ID and only reports the fallback model after the turn.
This was reproduced on Copilot SDK 1.0.15 with runtime 1.0.89. The observation is based on SDK-reported per-API-call usage, not on asking the model to identify itself.
Environment
| Component | Version / configuration |
|---|---|
| SDK | @github/copilot-sdk 1.0.15 |
Actual runtime, from client.getStatus() |
1.0.89 |
| Protocol version | 3 |
| Node.js | 24.22.0 |
| OS | Windows |
| Mode | copilot-cli |
| Authentication | Authenticated Copilot client; no BYOK provider |
| Requested model | invalid-model |
| Reasoning effort | medium |
| Automatic routing | Usage events reported isAuto: false |
| BYOK | Usage events reported isByok: false |
| Observation date | 2026-10-19 |
The SDK model catalog contained 16 models. invalid-model was absent. A valid control model, gpt-5.6-terra, was present and executed without substitution.
Steps to reproduce
Use an empty directory outside a source repository and an existing valid Copilot login. No repository files or external tools are needed. Ensure that no BYOK provider override is configured.
Install the observed SDK version:
npm init -y
npm install @github/[email protected] [email protected]
Save the following as repro.mjs, then run node repro.mjs.
Standalone SDK reproduction
import { randomUUID } from 'node:crypto';
import { CopilotClient, defineTool } from '@github/copilot-sdk';
import { z } from 'zod';
const requestedModel = 'invalid-model';
const usage = [];
const notices = [];
const modelChanges = [];
let verdict;
const client = new CopilotClient({ mode: 'copilot-cli' });
try {
await client.start();
const models = await client.listModels();
console.log({
requestedModel,
advertised: models.some((model) => model.id === requestedModel),
});
const session = await client.createSession({
sessionId: randomUUID(),
model: requestedModel,
reasoningEffort: 'medium',
systemMessage: {
mode: 'replace',
content: 'Call submit_verdict with passed set to true. Do not use other tools.',
},
tools: [defineTool('submit_verdict', {
description: 'Submit the probe verdict.',
parameters: z.object({ passed: z.boolean() }),
skipPermission: true,
handler(args) {
verdict = args;
return 'Grade submitted. Do not respond further.';
},
})],
availableTools: ['submit_verdict'],
onPermissionRequest: () => ({
kind: 'reject',
feedback: 'The probe denies other tool and file access.',
}),
infiniteSessions: { enabled: false },
enableSessionStore: false,
enableSessionTelemetry: false,
skipCustomInstructions: true,
enableConfigDiscovery: false,
enableSkills: false,
enableFileHooks: false,
onEvent(event) {
if (event.type === 'assistant.usage') {
const { model, isAuto, isByok } = event.data;
usage.push({ model, isAuto, isByok });
}
if (event.type === 'session.warning' || event.type === 'session.error') {
notices.push({ type: event.type, message: event.data.message });
}
if (event.type === 'session.model_change') {
modelChanges.push(event.data);
}
},
});
const selectedAfterCreate = (await session.rpc.model.getCurrent()).modelId;
await session.sendAndWait({ prompt: 'Submit the verdict now.' }, 45_000);
console.log(JSON.stringify({
requestedModel,
selectedAfterCreate,
selectedAfterTurn: (await session.rpc.model.getCurrent()).modelId,
usage,
notices,
modelChanges,
verdict,
}, null, 2));
} finally {
const errors = await client.stop();
if (errors.length) throw new AggregateError(errors, 'SDK cleanup failed.');
}
The observer is provided in createSession({ onEvent }), with an explicit session ID, so it is installed before session creation completes. The absence of a warning is not explained by attaching an observer after the turn.
For a control, replace invalid-model with a model ID returned by listModels() for the account.
Expected behavior
For applications that explicitly pin a model, there should be a supported way to reject an unavailable model rather than execute on a different one. A strict-model / no-fallback option would address this without necessarily changing the default behavior for existing applications.
If fallback remains enabled, it should be observable:
- Emit a warning and/or a model-change event identifying the requested and effective models and the reason.
- Document when the effective model is resolved and why the initial
getCurrent()value may not represent the model that will execute. - Make it possible to enforce the requested model without relying on a successful inference call on the wrong model.
Actual behavior and captured evidence
The live capture produced the following relevant fields:
{
"requestedModel": "invalid-model",
"sdkRequestModel": "invalid-model",
"selectedAfterCreate": "invalid-model",
"selectedAfterTurn": "claude-sonnet-5",
"usage": [
{ "model": "claude-sonnet-5", "isAuto": false, "isByok": false },
{ "model": "claude-sonnet-5", "isAuto": false, "isByok": false }
],
"notices": [],
"modelChanges": [],
"verdict": { "passed": true }
}
listModels() did not advertise invalid-model. Nevertheless, both API calls used claude-sonnet-5, the tool submitted a successful verdict, and the SDK did not report a session warning, session error, or model-change event.
claude-sonnet-5 is the fallback observed in this environment for this particular execution, not a claim about a universal or hard-coded default.
Impact
- Evaluations can execute on a different judge model while appearing to honor an explicit configuration.
- A typo or removed model ID can change model behavior, cost, and reproducibility without a configuration error.
- Inspecting only the initial selected model is insufficient to enforce model identity.
- Consumers may incorrectly attribute usage to the requested ID unless they independently capture per-call usage.
Related reports and documentation
- github/copilot-sdk#250: Allow disabling model fallback.
- github/copilot-sdk#378, a proposed model-validation change linked from that discussion, is closed and was not merged.
- The SDK usage documentation defines
assistant.usage.modelas the model identifier for the individual API call.
- Linguagem predominante
- Java
- Estrelas
- 10.7k
- Forks
- 1.5k
- Métricas de merge de PRs
- Nenhum PR com merge em 30d
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Sem modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de github/copilot-sdk
-
Rust: ElicitationRequest drops the tool call id from elicitation.requestedTalvez livre de novo Um pull request para esta issue foi fechado sem ser mesclado. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
github/copilot-sdk#2900 ·
-
Bump zod version to 4.6.6.Aberta
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 84/100
github/copilot-sdk#2876 · 1 comentário · 1 reação ·
-
Python ModelLimits drops max_output_tokens from model metadataTalvez livre de novo Um pull request para esta issue foi fechado sem ser mesclado. Abertabug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
github/copilot-sdk#2798 · 1 comentário ·
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 65/100
github/copilot-sdk#2759 ·
-
docs/AGENTS.md is repo authoring guidance, but everything in docs/ gets publishedTalvez livre de novo Um pull request para esta issue foi fechado sem ser mesclado. Abertadocumentation
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 85/100
github/copilot-sdk#2758 ·
Todas as issues de github/copilot-sdk
Issues semelhantes
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 75/100
ls1intum/thesis-management#1473 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
LunaDeerMC/Dominion#301 · 1 comentário ·
-
[FEATURE] Expose external refresh, node-level translog and warmer statsTalvez já em andamento Um pull request vinculado a esta issue está aberto ou já foi mesclado. Abertauntriaged
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 68/100
opensearch-project/opensearch-prometheus-exporter#680 ·
Mantenedores costumam responder em até 2 dias
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 85/100
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 66/100
Mantenedores costumam responder em até 2 dias