nexu-io/open-design · error · Error
no OpenAI credential - configure an API key in Settings or s
Error message
no OpenAI credential - configure an API key in Settings or set OPENAI_API_KEY.
What it means
Thrown at the top of renderOpenAIImage when credentials.apiKey is falsy. The renderer needs a Bearer key to call POST {baseUrl}/v1/images/generations (or the Azure deployment path); without one it refuses before any network call. The constant OPENAI_IMAGE_NO_CREDENTIAL_MESSAGE is reused so the same text reaches image and (via a sibling check) other OpenAI surfaces.
Source
Thrown at apps/daemon/src/media/index.ts:900
bodyTimeout: OPENAI_IMAGE_BODY_TIMEOUT_MS,
});
function withMediaRequestInit(
ctx: Pick<MediaContext, 'requestInit'>,
init: RequestInit = {},
): RequestInit {
return {
...ctx.requestInit,
...init,
};
}
const OPENAI_IMAGE_NO_CREDENTIAL_MESSAGE =
'no OpenAI credential - configure an API key in Settings or set OPENAI_API_KEY.';
async function renderOpenAIImage(ctx: MediaContext, credentials: ProviderConfig): Promise<RenderResult> {
if (!credentials.apiKey) {
throw new Error(OPENAI_IMAGE_NO_CREDENTIAL_MESSAGE);
}
const rawBase = credentials.baseUrl || 'https://api.openai.com/v1';
const azure = detectAzureEndpoint(rawBase);
const url = buildOpenAIImageUrl(rawBase, azure);
const body: Record<string, unknown> = {
prompt: ctx.prompt || 'A high-quality reference image.',
n: 1,
size: openaiSizeFor(ctx.model, ctx.aspect),
};
// For non-Azure calls, include `model` in the body. Azure infers it
// from the deployment in the path so omitting it keeps payloads
// compatible across both flavors. The wire-name (post-alias) goes
// on the body so the user's alias from issue #1277 reaches the API.
if (!azure) {
body.model = ctx.wireModel;
}
// Capability branches key off the CATALOG id (not the alias) so aView on GitHub (pinned to 5be4028344)
Solutions
- Set OPENAI_API_KEY in the daemon's environment, or add the key under Settings -> Media Providers -> OpenAI.
- If using Azure OpenAI, populate the same apiKey field with the Azure deployment key (baseUrl drives the Azure detection).
- Restart the daemon after setting the env var so the new value is read.
- Verify with od media providers (or the settings UI) that the OpenAI row shows a configured key.
Example fix
// before od media generate --surface image --model dall-e-3 --prompt "..." // after export OPENAI_API_KEY=sk-... od media generate --surface image --model dall-e-3 --prompt "..."
Defensive patterns
Strategy: validation
Validate before calling
function ensureOpenAICredential(creds: {apiKey?: string} | null): asserts creds is { apiKey: string } {
if (!creds?.apiKey) {
throw new Error('no OpenAI credential - configure an API key in Settings or set OPENAI_API_KEY.');
}
}
ensureOpenAICredential(credentials); Type guard
function hasOpenAIKey(c: {apiKey?:string} | null): c is { apiKey: string } {
return Boolean(c && typeof c.apiKey === 'string' && c.apiKey.length > 0);
} Prevention
- Pre-flight the OpenAI credential in the UI/CLI before listing dall-e/gpt-image models as runnable.
- Surface a missing-key badge next to OpenAI-dependent models in the picker.
- Document that the daemon process must have OPENAI_API_KEY exported (BYOK runs do not inherit login shell env).
When it happens
Trigger: Selecting an OpenAI image model (dall-e-*, gpt-image-*) with no OpenAI provider credential stored and no OPENAI_API_KEY env var; credentials saved under a different provider key; Settings page saved an empty key.
Common situations: Fresh install; user moved env vars out of the shell rc; key was deleted from the credentials store but the UI still shows the model as selectable; BYOK path where the daemon process does not inherit the user's shell env.
Related errors
- no ImageRouter API key — configure it in Settings or set OD_
- no OpenAI credential — configure an API key in Settings or s
- ${tag} ${resp.status}: ${truncate(text, 240)}
- openai non-JSON response: ${truncate(text, 200)}
- openai response had no data[0]
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/32614c42de270de1.
Report an issue: GitHub.