{"record":{"id":"475f9c2421dd7891","repo":"windmill-labs/windmill","slug":"missing-a-model-to-test","errorCode":null,"errorMessage":"Missing a model to test","messagePattern":"Missing a model to test","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/lib.ts","lineNumber":588,"sourceCode":"\tabortController,\n\tmessages,\n\taiProvider\n}: {\n\tapiKey?: string\n\tworkspace?: string\n\tresourcePath?: string\n\tmodel: string | undefined\n\tmessages: ChatCompletionMessageParam[]\n\tabortController: AbortController\n\taiProvider: AIProvider\n}) {\n\tif (!apiKey && !resourcePath) {\n\t\tthrow new Error('API key or resource path is required')\n\t}\n\tconst modelToTest = model ?? AI_PROVIDERS[aiProvider].defaultModels[0]\n\n\tif (!modelToTest) {\n\t\tthrow new Error('Missing a model to test')\n\t}\n\n\t// getNonStreamingCompletion routes Anthropic-Messages-API models (native\n\t// Anthropic and Claude on Azure Foundry) through the Anthropic SDK and\n\t// everything else through OpenAI chat completions, so the test exercises the\n\t// same request shape the feature actually sends. The cap keeps max_tokens\n\t// under the Anthropic SDK's non-streaming pre-flight limit (~21k tokens),\n\t// which would otherwise reject the request before it is sent.\n\tawait getNonStreamingCompletion(messages, abortController, {\n\t\tapiKey,\n\t\tworkspace,\n\t\tresourcePath,\n\t\tforceModelProvider: {\n\t\t\tmodel: modelToTest,\n\t\t\tprovider: aiProvider\n\t\t},\n\t\tmaxTokensCap: METADATA_MAX_TOKENS\n\t})","sourceCodeStart":570,"sourceCodeEnd":606,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/lib.ts#L570-L606","documentation":"testKey() falls back to the provider's first default model (AI_PROVIDERS[aiProvider].defaultModels[0]) when no explicit model is passed. If that defaults array is empty or undefined the resolved model is falsy and the test cannot be executed, so it throws 'Missing a model to test'.","triggerScenarios":"Calling testKey() with model: undefined for a provider whose AI_PROVIDERS entry has an empty defaultModels array, or an aiProvider key that resolves to an entry without defaults.","commonSituations":"A newly added/custom provider registered without defaultModels; a typo'd or legacy aiProvider string; a customai resource path configured where no default model list applies.","solutions":["Pass an explicit model argument to testKey() so the default lookup is skipped.","Populate defaultModels[0] for the provider in the AI_PROVIDERS registry.","Verify the aiProvider string matches a registered provider with defaults.","If testing a customai resource path, supply the model the resource actually serves."],"exampleFix":"// before\nawait testKey({ aiProvider: 'customai', resourcePath, model: undefined, ... })\n// after\nawait testKey({ aiProvider: 'customai', resourcePath, model: 'gpt-4o', ... })","handlingStrategy":"validation","validationCode":"const defaults = AI_PROVIDERS[aiProvider]?.defaultModels ?? []\nconst modelToTest = model ?? defaults[0]\nif (!modelToTest) {\n  showToast('Select a model — this provider has no default')\n  return\n}","typeGuard":"function hasResolvableModel(provider: AIProvider, model?: string): boolean {\n  return Boolean(model) || Boolean(AI_PROVIDERS[provider]?.defaultModels?.length)\n}","tryCatchPattern":"try {\n  await testKey({ aiProvider, apiKey, model, messages, abortController })\n} catch (e) {\n  if (e.message === 'Missing a model to test') {\n    showToast('Pick a model manually for this provider')\n  } else throw e\n}","preventionTips":["Always register defaultModels for new providers in AI_PROVIDERS.","Let the user pick a model explicitly when defaults are absent.","Validate aiProvider strings against registered keys before calling."],"tags":["validation","configuration","model","copilot"],"backgroundTag":"missing-model-configuration","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}