{"record":{"id":"a675e651120d09ac","repo":"CherryHQ/cherry-studio","slug":"ovms-provider-requires-a-non-empty-baseurl-an-e","errorCode":null,"errorMessage":"OVMS provider requires a non-empty `baseURL`. An empty value would resolve fetch paths against the renderer process origin (app://, file://) and surface as opaque \"Failed to fetch\" errors.","messagePattern":"OVMS provider requires a non-empty `baseURL`\\. An empty value would resolve fetch paths against the renderer process origin \\(app://, file://\\) and surface as opaque \"Failed to fetch\" errors\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/provider/custom/ovms/ovmsProvider.ts","lineNumber":42,"sourceCode":"export interface OvmsProvider extends ProviderV3 {\n  (modelId: string): LanguageModelV3\n  languageModel(modelId: string): LanguageModelV3\n  embeddingModel(modelId: string): EmbeddingModelV3\n  imageModel(modelId: string): ImageModelV3\n}\n\n/**\n * Unified OVMS provider — chat, embedding, and image off one `ProviderV3`,\n * mirroring `newapi-provider.ts`. OVMS is a local OpenVINO Model Server with\n * NO auth, so headers carry only what the caller passes (no `Authorization`).\n * Chat/embedding hit `settings.baseURL`; the image model keeps its bespoke\n * single-shot behavior via `createImageGenerationModel + createOvmsTransport`\n * aimed at `settings.imageBaseURL`.\n */\nexport function createOvmsProvider(settings: OvmsProviderSettings = {}): OvmsProvider {\n  const { baseURL, fetch: customFetch } = settings\n  if (!baseURL) {\n    throw new Error(\n      'OVMS provider requires a non-empty `baseURL`. An empty value would resolve fetch paths against the renderer process origin (app://, file://) and surface as opaque \"Failed to fetch\" errors.'\n    )\n  }\n\n  const authHeaders = () => ({ ...settings.headers })\n\n  const url = ({ path }: { path: string; modelId: string }) => `${withoutTrailingSlash(baseURL)}${path}`\n\n  const createChatModel = (modelId: string) =>\n    new OpenAICompatibleChatLanguageModel(modelId, {\n      provider: `${OVMS_PROVIDER_NAME}.chat`,\n      url,\n      headers: authHeaders,\n      fetch: customFetch\n    })\n\n  const transport = createOvmsTransport({\n    baseURL: settings.imageBaseURL || DEFAULT_OVMS_BASE_URL","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/provider/custom/ovms/ovmsProvider.ts#L24-L60","documentation":"A fail-fast guard inside createOvmsProvider(): if settings.baseURL is empty, the provider refuses to construct. An empty baseURL would make the OpenAI-compatible SDK resolve fetch paths against the Electron renderer origin (app:// or file://), producing opaque 'Failed to fetch' errors that hide the real cause. Throwing at construction converts that into a clear, actionable message.","triggerScenarios":"createOvmsProvider({}) or createOvmsProvider({ baseURL: '' }) — i.e. the OVMS provider is instantiated without a host. Happens at provider factory time when settings haven't been loaded or the user added an OVMS provider without filling in the host.","commonSituations":"New OVMS provider added via UI but the host field left blank, settings migration leaving baseURL undefined, or programmatic provider construction without the host.","solutions":["Set a non-empty OVMS host before constructing the provider (e.g. http://localhost:8000/v3/ for chat/embedding).","If constructing programmatically, read the host from BootConfig/Preference before calling createOvmsProvider.","In the UI, mark the baseURL field required and disable 'Save' until filled.","If the user genuinely has no host yet, do not instantiate the provider — defer construction until settings are complete."],"exampleFix":"// before\nconst provider = createOvmsProvider({ baseURL: settings.ovms?.baseURL })\n// after — validate before construction\nif (!settings.ovms?.baseURL) throw new Error('OVMS host is required')\nconst provider = createOvmsProvider({ baseURL: settings.ovms.baseURL })","handlingStrategy":"validation","validationCode":"if (!settings.baseURL) {\n  throw new Error('OVMS host is required (e.g. http://localhost:8000/v3/)')\n}\nconst provider = createOvmsProvider(settings)","typeGuard":"export function isOvmsBaseURLError(e: unknown): boolean {\n  return e instanceof Error && /OVMS provider requires a non-empty `baseURL`/.test(e.message)\n}","tryCatchPattern":"try {\n  createOvmsProvider(settings)\n} catch (e) {\n  if (isOvmsBaseURLError(e)) {\n    // prompt the user for the OVMS host before retrying\n  }\n  throw e\n}","preventionTips":["Make the OVMS host field required in the provider UI.","Load host from Preference/BootConfig before constructing the provider.","Never instantiate OVMS with an empty baseURL just to 'fill it in later'."],"tags":["validation","configuration","ovms","base-url","fail-fast"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}