nexu-io/open-design · error · Error

openai response had neither b64_json nor url

Error message

openai response had neither b64_json nor url

What it means

Thrown by renderOpenAIImage when data[0] exists but contains neither b64_json nor url. The OpenAI image contract requires exactly one of these fields per entry; their absence indicates a malformed or partial upstream response (e.g. a moderation refusal that returned an empty entry, or an API version that uses a different field name).

Source

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

  }
  let data: any;
  try {
    data = JSON.parse(text);
  } catch {
    throw new Error(`openai non-JSON response: ${truncate(text, 200)}`);
  }
  const entry = data && Array.isArray(data.data) ? data.data[0] : null;
  if (!entry) throw new Error('openai response had no data[0]');
  let bytes;
  if (entry.b64_json) {
    bytes = Buffer.from(entry.b64_json, 'base64');
  } else if (entry.url) {
    const imgResp = await fetch(entry.url, withMediaRequestInit(ctx));
    if (!imgResp.ok) throw new Error(`openai image fetch ${imgResp.status}`);
    const arr = await imgResp.arrayBuffer();
    bytes = Buffer.from(arr);
  } else {
    throw new Error('openai response had neither b64_json nor url');
  }

  const tag = azure ? 'azure-openai' : 'openai';
  return {
    bytes,
    providerNote: `${tag}/${ctx.wireModel} · ${ctx.aspect} · ${bytes.length} bytes`,
    suggestedExt: '.png',
  };
}

async function renderImageRouterImage(ctx: MediaContext, credentials: ProviderConfig): Promise<RenderResult> {
  if (!credentials.apiKey) {
    throw new Error(
      'no ImageRouter API key — configure it in Settings or set OD_IMAGEROUTER_API_KEY',
    );
  }
  const baseUrl = (credentials.baseUrl || IMAGEROUTER_DEFAULT_BASE_URL).trim();
  const wireModel = (credentials.model || ctx.wireModel).trim();

View on GitHub (pinned to 5be4028344)

Solutions

  1. Inspect data[0] to see which fields it does carry, and adjust the renderer or the deployment to emit b64_json or url.
  2. Rephrase the prompt to avoid moderation-triggering content.
  3. Switch to a GA model id with a stable response schema.
  4. For Azure, set the deployment output format to PNG/JPEG so b64_json is populated.

Example fix

// before: preview deployment returns {data:[{revised_prompt:'...'}]}
// after: use GA deployment that returns {data:[{b64_json:'...'}]}
od media generate --surface image --model gpt-image-1 --prompt "..."
Defensive patterns

Strategy: try-catch

Validate before calling

function isOpenAIImageEntry(entry: unknown): entry is { b64_json?: string; url?: string } {
  return Boolean(entry && typeof entry === 'object' && ((entry as any).b64_json || (entry as any).url));
}

Type guard

function isOpenAIImageEntry(entry: unknown): entry is { b64_json?: string; url?: string } {
  return Boolean(entry && typeof entry === 'object' && ((entry as any).b64_json || (entry as any).url));
}

Try / catch

try {
  return await renderOpenAIImage(ctx, credentials);
} catch (err) {
  const m = err instanceof Error ? err.message : '';
  if (m === 'openai response had neither b64_json nor url') {
    // usually moderation or schema skew; do not retry the same prompt
    throw new UserActionableError('OpenAI returned an empty image entry (possibly moderation). Rephrase the prompt.');
  }
  throw err;
}

Prevention

When it happens

Trigger: Content moderation triggered and the entry was returned empty; using a beta/preview API version that returns a different field (e.g. 'image' instead of 'b64_json'); upstream returned a stub entry on partial failure; Azure deployment configured with output-format the daemon does not recognize.

Common situations: Prompt tripped safety filters; preview model on Azure with a non-standard response format; misconfigured deployment returning metadata-only entries.

Related errors


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