{"record":{"id":"611c5b318587f02f","repo":"nexu-io/open-design","slug":"custom-image-api-base-url-required-configure-an","errorCode":null,"errorMessage":"Custom Image API base URL required — configure an OpenAI-compatible /v1/images/generations or /v1/images/edits endpoint in Settings","messagePattern":"Custom Image API base URL required — configure an OpenAI-compatible /v1/images/generations or /v1/images/edits endpoint in Settings","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":1056,"sourceCode":"    headers: {\n      'authorization': `Bearer ${credentials.apiKey}`,\n      'content-type': 'application/json',\n    },\n    body: JSON.stringify(body),\n  }));\n  const data = await parseOpenAICompatibleJson(resp, 'imagerouter video');\n  const bytes = await bytesFromOpenAICompatibleData(data, 'imagerouter video', ctx.requestInit);\n  return {\n    bytes,\n    providerNote: `imagerouter/${wireModel} · ${imageRouterSizeFor(ctx.aspect, 'video')} · ${seconds === 'auto' ? 'auto' : `${seconds}s`} · ${bytes.length} bytes`,\n    suggestedExt: '.mp4',\n  };\n}\n\nasync function renderCustomOpenAIImage(ctx: MediaContext, credentials: ProviderConfig): Promise<RenderResult> {\n  const baseUrl = (credentials.baseUrl || '').trim();\n  if (!baseUrl) {\n    throw new Error(\n      'Custom Image API base URL required — configure an OpenAI-compatible /v1/images/generations or /v1/images/edits endpoint in Settings',\n    );\n  }\n  const wireModel = (\n    credentials.model\n    || (ctx.wireModel !== CUSTOM_IMAGE_MODEL_ID ? ctx.wireModel : '')\n  ).trim();\n  if (!wireModel) {\n    throw new Error(\n      'Custom Image API model required — configure the provider model in Settings',\n    );\n  }\n\n  const headers: Record<string, string> = {\n    'content-type': 'application/json',\n  };\n  if (credentials.apiKey) {\n    headers.authorization = `Bearer ${credentials.apiKey}`;","sourceCodeStart":1038,"sourceCodeEnd":1074,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L1038-L1074","documentation":"Thrown at the top of renderCustomOpenAIImage when credentials.baseUrl is empty after trimming. The 'custom-image' provider is a user-supplied OpenAI-compatible endpoint (/v1/images/generations or /v1/images/edits); unlike first-party providers there is no default base URL, so the user MUST configure one in Settings. The renderer cannot even form the request URL without it.","triggerScenarios":"Selecting --model custom-image with no baseUrl configured; user filled in the model field but left the base URL blank; baseUrl contains only whitespace.","commonSituations":"User assumed the custom provider defaults to api.openai.com; settings form submitted with the URL field empty; a profile switch cleared the custom provider config.","solutions":["Open Settings -> Media Providers -> Custom Image API and enter the full OpenAI-compatible base URL (e.g. https://my-gateway.example.com/v1).","Ensure the URL points at the /v1 root, not at /v1/images/generations itself — the path is appended by buildOpenAIImageUrl.","After saving, re-run the generation; if it still fails, the next guard (model required) or the upstream status error will fire.","If you actually want first-party OpenAI, switch --model to dall-e-*/gpt-image-* instead of custom-image."],"exampleFix":"// before: no baseUrl configured\nod media generate --surface image --model custom-image --prompt \"...\"\n// after: configure baseUrl in Settings, then\nod media generate --surface image --model custom-image --prompt \"...\"","handlingStrategy":"validation","validationCode":"function ensureCustomImageBaseUrl(creds: {baseUrl?: string}): asserts creds is { baseUrl: string } {\n  const base = (creds.baseUrl || '').trim();\n  if (!base) {\n    throw new Error('Custom Image API base URL required — configure an OpenAI-compatible /v1/images/generations or /v1/images/edits endpoint in Settings');\n  }\n}\nensureCustomImageBaseUrl(credentials);","typeGuard":"function hasCustomBaseUrl(c: {baseUrl?:string}): c is { baseUrl: string } {\n  return Boolean(c && typeof c.baseUrl === 'string' && c.baseUrl.trim().length > 0);\n}","tryCatchPattern":null,"preventionTips":["Always pair --model custom-image with a baseUrl in Settings; there is no default.","Point baseUrl at the gateway's /v1 root, not at /v1/images/generations (the path is appended).","Smoke-test the endpoint with curl before configuring the daemon."],"tags":["media","provider-config","custom-image","configuration"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}