nexu-io/open-design · error · Error

no xAI credentials — sign in with your SuperGrok subscriptio

Error message

no xAI credentials — sign in with your SuperGrok subscription (in OD or via `hermes auth add xai-oauth`), set XAI_API_KEY, or configure a key in Settings

What it means

Thrown at the top of `renderGrokImage` when no xAI credential is available. xAI auth has three sources — SuperGrok OAuth (via OD sign-in or `hermes auth add xai-oauth`), the `XAI_API_KEY` env var, or a key in Settings — and the message lists all three so the user can pick whichever they have.

Source

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

//
// Docs: https://docs.x.ai/developers/model-capabilities/{images,video}/generation
//   * Image: POST /v1/images/generations — synchronous, returns
//            {data:[{b64_json|url}]}; we ask for b64_json so the bytes
//            arrive in one round-trip.
//   * Video: POST /v1/videos/generations — may return the finished video
//            inline ({status:'done', video:{url}}) or an async stub
//            ({id, status:'pending'}); in the async case we poll
//            GET /v1/videos/{id} until status flips to done/failed.
//
// xAI's video model produces native audio (background music + SFX +
// ambient) synchronised with the visual; that's the headline
// differentiator vs Seedance and Sora and is why grok-imagine-video
// declares the `audio` capability.
// ---------------------------------------------------------------------------

async function renderGrokImage(ctx: MediaContext, credentials: ProviderConfig): Promise<RenderResult> {
  if (!credentials.apiKey) {
    throw new Error(
      'no xAI credentials — sign in with your SuperGrok subscription (in OD or via `hermes auth add xai-oauth`), set XAI_API_KEY, or configure a key in Settings',
    );
  }
  const baseUrl = (credentials.baseUrl || 'https://api.x.ai/v1').replace(/\/$/, '');

  const aspectRatio = grokAspectFor(ctx.aspect);
  const body = {
    model: ctx.wireModel,
    prompt: ctx.prompt || 'A high-quality reference image.',
    n: 1,
    aspect_ratio: aspectRatio,
    response_format: 'b64_json',
  };
  const resp = await fetch(`${baseUrl}/images/generations`, withMediaRequestInit(ctx, {
    method: 'POST',
    headers: {
      'authorization': `Bearer ${credentials.apiKey}`,
      'content-type': 'application/json',

View on GitHub (pinned to 5be4028344)

Solutions

  1. Sign in with SuperGrok via OD Settings, or run `hermes auth add xai-oauth`, or set `XAI_API_KEY`, or enter a key in Settings → Providers → xAI.
  2. Restart the daemon after adding/refreshing the credential so `resolveProviderConfig` re-reads it.
  3. If using OAuth and it expired, re-run the OAuth flow.

Example fix

// before
od media generate --surface image --model grok-imagine-image --prompt '...'

// after (option A: env)
export XAI_API_KEY=xai-xxxx
od media generate --surface image --model grok-imagine-image --prompt '...'

// after (option B: OAuth)
hermes auth add xai-oauth
Defensive patterns

Strategy: validation

Validate before calling

function ensureGrokCreds(credentials: ProviderConfig): void {
  if (!credentials.apiKey) {
    throw new Error('no xAI credentials — sign in with SuperGrok, set XAI_API_KEY, or configure a key in Settings');
  }
}
// call before renderGrokImage(ctx, credentials)
ensureGrokCreds(credentials);

Type guard

function hasGrokCreds(c: ProviderConfig): c is ProviderConfig & { apiKey: string } {
  return typeof c.apiKey === 'string' && c.apiKey.length > 0;
}

Prevention

When it happens

Trigger: `credentials.apiKey` is falsy after `resolveProviderConfig('grok')` resolves the key from OAuth, `XAI_API_KEY`, or Settings, while the selected model is a `provider: 'grok'` image model (e.g. `grok-imagine-image`).

Common situations: (1) User picked a Grok Imagine model before signing in; (2) SuperGrok OAuth token expired and wasn't refreshed; (3) `XAI_API_KEY` set in a different shell than the daemon; (4) key cleared from Settings but model selection persisted.

Related errors


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