nexu-io/open-design · error · Error

unsupported audioKind: ${audioKind}. Allowed: music | speech

Error message

unsupported audioKind: ${audioKind}. Allowed: music | speech | sfx.

What it means

Thrown by the media dispatcher's input-validation block when surface is 'audio' and a non-empty audioKind is supplied that is not one of the three allowed kinds. The set AUDIO_KINDS = {'music','speech','sfx'} is the only accepted vocabulary. The check runs before model resolution, so no provider is contacted.

Source

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

    image,
    requestInit,
    workspaceId,
    onProviderRequestSettled,
  } = args;

  if (!projectRoot) throw new Error('projectRoot required');
  if (!projectsRoot) throw new Error('projectsRoot required');
  if (typeof projectId !== 'string' || !projectId) {
    throw new Error('projectId required');
  }
  if (!SURFACES.has(surface)) {
    throw new Error(`unsupported surface: ${surface}`);
  }
  if (typeof model !== 'string' || !model) {
    throw new Error('model required');
  }
  if (surface === 'audio' && audioKind && !AUDIO_KINDS.has(audioKind)) {
    throw new Error(
      `unsupported audioKind: ${audioKind}. Allowed: music | speech | sfx.`,
    );
  }
  // Arbitrary fal.ai model paths (e.g. "fal-ai/flux/dev") bypass the
  // catalog so users can reach any model on fal without waiting for a
  // catalog entry. Surface comes from the caller; no cross-surface guard
  // is needed because the fal renderer reads ctx.surface directly.
  let def = findMediaModel(model);
  let isFalCustomPath = false;
  let isCatalogBypass = false;
  if (!def) {
    if (/^fal-ai\//.test(model)) {
      isFalCustomPath = true;
      def = {
        id: model,
        label: model,
        hint: 'Fal.ai',
        provider: 'fal',

View on GitHub (pinned to 5be4028344)

Solutions

  1. Re-run with --audio-kind set to one of music, speech, or sfx (omit the flag entirely to default to music).
  2. If invoking programmatically, source the allowed set from the catalog (e.g. GET /api/media/models filtered by surface=audio) rather than hard-coding.
  3. Update the calling agent's prompt or tool schema so the audioKind enum matches the backend exactly.

Example fix

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

Strategy: validation

Validate before calling

const AUDIO_KINDS = new Set(['music', 'speech', 'sfx']);
function validateAudioKind(kind: string | undefined): string | undefined {
  if (kind === undefined || kind === '') return undefined;
  if (!AUDIO_KINDS.has(kind)) {
    throw new Error(`unsupported audioKind: ${kind}. Allowed: music | speech | sfx.`);
  }
  return kind;
}
// call before od media generate:
const safeKind = validateAudioKind(args.audioKind);

Type guard

function isAudioKind(v: unknown): v is 'music' | 'speech' | 'sfx' {
  return typeof v === 'string' && ['music', 'speech', 'sfx'].includes(v);
}

Prevention

When it happens

Trigger: Calling od media generate (or POST /api/media/generate) with --surface audio and an --audio-kind value other than music/speech/sfx (e.g. 'voice', 'instrumental', 'sound', or a typo like 'musci'). An agent hallucinating an audio-kind argument also lands here.

Common situations: Agent-generated CLI invocations inventing vocabulary; UI dropdown out of sync with the backend enum; user assuming 'voice' is valid because speech TTS uses voices; copy/paste from older docs that listed different kinds.

Related errors


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