windmill-labs/windmill · error
Cannot test API key for Custom AI, only resource path is sup
Error message
Cannot test API key for Custom AI, only resource path is supported
What it means
Custom AI (customai, an Azure-OpenAI-style provider) authenticates via a resource path header, not an API key. testKey() explicitly rejects apiKey-only configs for this provider because a key alone produces an untestable request; only resourcePath is supported for the test.
Source
Thrown at frontend/src/lib/components/copilot/lib.ts:996
}
const fetchOptions: {
signal: AbortSignal
headers: Record<string, string>
} = {
signal: abortController.signal,
headers: {
'X-Provider': provider
}
}
if (options?.resourcePath) {
fetchOptions.headers = {
...fetchOptions.headers,
'X-Resource-Path': options.resourcePath
}
} else if (options?.apiKey) {
if (provider === 'customai') {
throw new Error('Cannot test API key for Custom AI, only resource path is supported')
}
fetchOptions.headers = {
...fetchOptions.headers,
'X-API-Key': options.apiKey
}
}
const openaiClient = options?.apiKey
? createOpenAIProxyClient(getAiProxyBaseURL())
: options?.workspace
? workspaceAIClients.createOpenaiClient(options.workspace)
: workspaceAIClients.getOpenaiClient()
const completion = await openaiClient.chat.completions.create(config, fetchOptions)
response = completion.choices?.[0]?.message.content || ''
return response
}
View on GitHub (pinned to e474e8803c)
Solutions
- Set the Custom AI resource path in the AI settings and re-run the test.
- Clear the API key field if the form routes the request into the apiKey branch; ensure resourcePath is the populated credential.
- If you have only a key (no Azure-style resource), configure a plain OpenAI-compatible provider instead of customai.
- Check the workspace AI config stored value actually contains resourcePath (it may not have been saved).
Example fix
// before
await testKey({ aiProvider: 'customai', apiKey: 'sk-...', ... })
// after
await testKey({ aiProvider: 'customai', resourcePath: 'my-resource/deployment', ... }) Defensive patterns
Strategy: validation
Validate before calling
if (provider === 'customai' && !resourcePath?.trim()) {
showToast('Custom AI requires a resource path to test')
return
} Type guard
function customaiConfigOk(opts: { provider: string; resourcePath?: string }): boolean {
return opts.provider !== 'customai' || Boolean(opts.resourcePath?.trim())
} Try / catch
try {
await testKey({ aiProvider: 'customai', resourcePath, ... })
} catch (e) {
if (e.message.includes('Custom AI')) {
showToast('Custom AI testing uses a resource path, not an API key')
} else throw e
} Prevention
- In the Custom AI settings form, require the resource-path field and de-emphasize the key field.
- Document per-provider credential requirements next to the inputs.
- Normalize migrated configs to attach resourcePath for customai entries.
When it happens
Trigger: Calling testKey() with aiProvider 'customai' and options.apiKey set while options.resourcePath is unset — e.g. pasting an API key into the Custom AI settings and clicking Test without filling the resource path.
Common situations: Users coming from OpenAI-style providers assume a key is the credential; a form that submits both fields but leaves resourcePath blank; migrated configs that carried only a key.
Related errors
- API key or resource path is required
- Invalid AI agent provider configuration: ${errors.join('\n')
- Missing a model to test
- Invalid S3 mode format: {}
- WORKER_SUFFIX must only contain ASCII letters, digits and un
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/7e323d05793e90e4.
Report an issue: GitHub.