Bug Summary
When a Custom OpenAI-compatible API endpoint (e.g. local LLM, proxy, Manifest) is configured via gptBaseUrl + gptBaseUrlKey, the AI Inline Completion and Chat features fail with "Invalid API key (401)" even though the Endpoint-Test button succeeds. The root cause is that the frontend sends provider: "openai" for custom endpoints, but the backend resolves credentials from gptKey instead of gptBaseUrlKey.
Steps to Reproduce
- Leave the "OpenAI / ChatGPT API key" (
gptKey) empty.
- Enter a "Custom API Base URL" (e.g.
http://192.168.1.10:11434/v1).
- Enter a valid "Custom API key" (
gptBaseUrlKey, e.g. mnfst_xxx for Manifest).
- Save the adapter config and restart.
- Open the JavaScript editor → click the AI Chat button.
- Observe: Endpoint test returns "ok", but actual chat/completion returns "Invalid API key (401)".
Expected Behavior
When gptBaseUrl is configured and gptBaseUrlKey contains a key, the adapter should use gptBaseUrlKey for requests sent to that custom base URL, not gptKey.
Actual Behavior
The frontend (AiInlineProvider-CaHd2G2Q.js) maps provider: "custom" → provider: "openai" + baseUrl.
let providers = ['custom','openai','deepseek','anthropic','gemini'];
for (let e of providers) {
let t = n.providers.find(t => t.provider === e);
if (t) {
d = e === 'custom' ? 'openai' : e; // "custom" becomes "openai"
f = t.baseUrl || '';
break;
}
}
The backend (aiProviderResolver.ts / build/lib/aiProviderResolver.js) then resolves credentials based on provider:
const PROVIDER_KEY_FIELD = {
openai: 'gptKey', // ← wrong field for custom endpoint
anthropic: 'claudeKey',
gemini: 'geminiKey',
deepseek: 'deepseekKey',
custom: 'gptBaseUrlKey', // ← never reached
};
Since provider === 'openai' and gptKey is empty, the adapter sends an empty or wrong API key to the custom endpoint, resulting in 401.
Environment
- Adapter: ioBroker.javascript v10.1.3
- Node.js: v24.x (also reproducible on v22)
- Credential mode: both manual and manager affected
Workaround
As a workaround, enter the Custom API key into the "OpenAI / ChatGPT API key" field (gptKey) instead of the "Custom API key" field (gptBaseUrlKey). This works because the backend treats custom endpoints as provider: 'openai' and reads from gptKey. However, this makes it impossible to use both OpenRouter (or real OpenAI) and a custom endpoint simultaneously, because both would share the same gptKey field.
Suggested Fix
Option A: In aiProviderResolver.resolveProviderCredentials, check if a custom baseUrl is present alongside provider === 'openai'. If so, prefer gptBaseUrlKey over gptKey.
Option B: Preserve provider: 'custom' through the entire flow instead of rewriting it to 'openai' in the frontend. Then the backend naturally resolves gptBaseUrlKey.
Option C: Add a dedicated provider type mapping: when baseUrl is set and differs from the default OpenAI URL, automatically use the custom credentials.
Related
Bug Summary
When a Custom OpenAI-compatible API endpoint (e.g. local LLM, proxy, Manifest) is configured via
gptBaseUrl+gptBaseUrlKey, the AI Inline Completion and Chat features fail with "Invalid API key (401)" even though the Endpoint-Test button succeeds. The root cause is that the frontend sendsprovider: "openai"for custom endpoints, but the backend resolves credentials fromgptKeyinstead ofgptBaseUrlKey.Steps to Reproduce
gptKey) empty.http://192.168.1.10:11434/v1).gptBaseUrlKey, e.g.mnfst_xxxfor Manifest).Expected Behavior
When
gptBaseUrlis configured andgptBaseUrlKeycontains a key, the adapter should usegptBaseUrlKeyfor requests sent to that custom base URL, notgptKey.Actual Behavior
The frontend (
AiInlineProvider-CaHd2G2Q.js) mapsprovider: "custom"→provider: "openai"+baseUrl.The backend (
aiProviderResolver.ts/build/lib/aiProviderResolver.js) then resolves credentials based onprovider:Since
provider === 'openai'andgptKeyis empty, the adapter sends an empty or wrong API key to the custom endpoint, resulting in 401.Environment
Workaround
As a workaround, enter the Custom API key into the "OpenAI / ChatGPT API key" field (
gptKey) instead of the "Custom API key" field (gptBaseUrlKey). This works because the backend treats custom endpoints asprovider: 'openai'and reads fromgptKey. However, this makes it impossible to use both OpenRouter (or real OpenAI) and a custom endpoint simultaneously, because both would share the samegptKeyfield.Suggested Fix
Option A: In
aiProviderResolver.resolveProviderCredentials, check if a custombaseUrlis present alongsideprovider === 'openai'. If so, prefergptBaseUrlKeyovergptKey.Option B: Preserve
provider: 'custom'through the entire flow instead of rewriting it to'openai'in the frontend. Then the backend naturally resolvesgptBaseUrlKey.Option C: Add a dedicated provider type mapping: when
baseUrlis set and differs from the default OpenAI URL, automatically use the custom credentials.Related