nexu-io/open-design · error · Error

model "${model}" is not registered for surface "${where}". A

Error message

model "${model}" is not registered for surface "${where}". Allowed: ${ids}.

What it means

Thrown after successful model lookup when the resolved catalog model is not in the allowed list for the requested surface (and, for audio, the resolved audioKind). modelsForSurface(surface, resolvedAudioKind) returns the per-surface allow-list; if def.id is absent the dispatcher refuses to render. This is the cross-surface guard for catalogued models — fal-ai/* and aihubmix-* bypasses are exempt because their defs are synthesized with the caller's surface.

Source

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

                : [],
      };
    } else {
      throw new Error(
        `unknown model: ${model}. Pass --model from the registered list (see /api/media/models), ` +
        `or pass a full fal-ai/* path (e.g. fal-ai/flux/dev) for any Fal model.`,
      );
    }
  }
  // Reject cross-surface combinations for catalogued models.
  const resolvedAudioKind =
    surface === 'audio' ? audioKind || 'music' : undefined;
  if (!isFalCustomPath && !isCatalogBypass) {
    const allowed = modelsForSurface(surface, resolvedAudioKind);
    if (!allowed.some((m) => m.id === def.id)) {
      const ids = allowed.map((m) => m.id).join(', ');
      const where =
        surface === 'audio' ? `audio · ${resolvedAudioKind}` : surface;
      throw new Error(
        `model "${model}" is not registered for surface "${where}". Allowed: ${ids}.`,
      );
    }
  }

  // Clamp registry-bound numeric inputs to their allowed buckets so a
  // hallucinated --length 9999999 doesn't reach a real provider as-is
  // when stubs are swapped for paid integrations.
  const lengthClamp =
    surface === 'video'
      ? def.provider === 'vela'
        ? {
            value:
              typeof length === 'number' && Number.isFinite(length)
                ? length
                : undefined,
            warning: null,
          }

View on GitHub (pinned to 5be4028344)

Solutions

  1. Read the 'Allowed' list in the error and switch --model to one of those ids.
  2. Switch --surface to the surface the chosen model is registered for.
  3. For audio, set --audio-kind to the kind the model supports (speech TTS models are usually speech-only).
  4. If you genuinely need an arbitrary Fal model on a different surface, use the fal-ai/* bypass form, which skips this guard.

Example fix

// before
od media generate --surface audio --audio-kind music --model gpt-4o-mini-tts --prompt "..."
// after
od media generate --surface audio --audio-kind speech --model gpt-4o-mini-tts --prompt "..."
Defensive patterns

Strategy: validation

Validate before calling

function modelAllowedForSurface(modelId: string, surface: 'image'|'video'|'audio', audioKind: 'music'|'speech'|'sfx'|undefined, allowed: {id:string}[]): boolean {
  const resolved = surface === 'audio' ? (audioKind || 'music') : undefined;
  // mirror dispatcher logic: skip for fal-ai/* and aihubmix-* bypasses
  if (/^fal-ai\//.test(modelId) || /^aihubmix-/.test(modelId)) return true;
  return allowed.some(m => m.id === modelId);
}

Type guard

function isModelForSurface(def: {id:string}, surface: string, allowed: {id:string}[]): boolean {
  return allowed.some(m => m.id === def.id);
}

Prevention

When it happens

Trigger: Using an image-only model with --surface video (or vice versa); using a TTS model that is registered only for audioKind=speech while passing --audio-kind music; resolving audio without a kind (defaults to 'music') for a speech-only model.

Common situations: Agent picks a familiar model id without checking which surface it belongs to; UI lets the user pair any model with any surface; user assumes 'dall-e-3' works for video because it is a famous media model.

Related errors


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