nexu-io/open-design · error · Error

Custom Image API base URL required — configure an OpenAI-com

Error message

Custom Image API base URL required — configure an OpenAI-compatible /v1/images/generations or /v1/images/edits endpoint in Settings

What it means

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.

Source

Thrown at apps/daemon/src/media/index.ts:1056

    headers: {
      'authorization': `Bearer ${credentials.apiKey}`,
      'content-type': 'application/json',
    },
    body: JSON.stringify(body),
  }));
  const data = await parseOpenAICompatibleJson(resp, 'imagerouter video');
  const bytes = await bytesFromOpenAICompatibleData(data, 'imagerouter video', ctx.requestInit);
  return {
    bytes,
    providerNote: `imagerouter/${wireModel} · ${imageRouterSizeFor(ctx.aspect, 'video')} · ${seconds === 'auto' ? 'auto' : `${seconds}s`} · ${bytes.length} bytes`,
    suggestedExt: '.mp4',
  };
}

async function renderCustomOpenAIImage(ctx: MediaContext, credentials: ProviderConfig): Promise<RenderResult> {
  const baseUrl = (credentials.baseUrl || '').trim();
  if (!baseUrl) {
    throw new Error(
      'Custom Image API base URL required — configure an OpenAI-compatible /v1/images/generations or /v1/images/edits endpoint in Settings',
    );
  }
  const wireModel = (
    credentials.model
    || (ctx.wireModel !== CUSTOM_IMAGE_MODEL_ID ? ctx.wireModel : '')
  ).trim();
  if (!wireModel) {
    throw new Error(
      'Custom Image API model required — configure the provider model in Settings',
    );
  }

  const headers: Record<string, string> = {
    'content-type': 'application/json',
  };
  if (credentials.apiKey) {
    headers.authorization = `Bearer ${credentials.apiKey}`;

View on GitHub (pinned to 5be4028344)

Solutions

  1. Open Settings -> Media Providers -> Custom Image API and enter the full OpenAI-compatible base URL (e.g. https://my-gateway.example.com/v1).
  2. Ensure the URL points at the /v1 root, not at /v1/images/generations itself — the path is appended by buildOpenAIImageUrl.
  3. After saving, re-run the generation; if it still fails, the next guard (model required) or the upstream status error will fire.
  4. If you actually want first-party OpenAI, switch --model to dall-e-*/gpt-image-* instead of custom-image.

Example fix

// before: no baseUrl configured
od media generate --surface image --model custom-image --prompt "..."
// after: configure baseUrl in Settings, then
od media generate --surface image --model custom-image --prompt "..."
Defensive patterns

Strategy: validation

Validate before calling

function ensureCustomImageBaseUrl(creds: {baseUrl?: string}): asserts creds is { baseUrl: string } {
  const base = (creds.baseUrl || '').trim();
  if (!base) {
    throw new Error('Custom Image API base URL required — configure an OpenAI-compatible /v1/images/generations or /v1/images/edits endpoint in Settings');
  }
}
ensureCustomImageBaseUrl(credentials);

Type guard

function hasCustomBaseUrl(c: {baseUrl?:string}): c is { baseUrl: string } {
  return Boolean(c && typeof c.baseUrl === 'string' && c.baseUrl.trim().length > 0);
}

Prevention

When it happens

Trigger: Selecting --model custom-image with no baseUrl configured; user filled in the model field but left the base URL blank; baseUrl contains only whitespace.

Common situations: 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.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/611c5b318587f02f. Report an issue: GitHub.