nexu-io/open-design · error · Error

no Volcengine Ark API key — configure it in Settings or set

Error message

no Volcengine Ark API key — configure it in Settings or set ARK_API_KEY

What it means

Thrown at the top of `renderVolcengineVideo` (Doubao Seedance) when no Volcengine Ark API key is available. The renderer cannot authenticate the `POST /api/v3/contents/generations/tasks` call, so it fails fast with an actionable message naming the `ARK_API_KEY` env var and the Settings UI.

Source

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

    suggestedExt: format === 'opus' ? '.ogg' : `.${format}`,
  };
}

// ---------------------------------------------------------------------------
// Provider: Volcengine Ark — Doubao Seedance 2.0 video.
//
// Docs:
//   POST /api/v3/contents/generations/tasks   → { id }
//   GET  /api/v3/contents/generations/tasks/{id} → { status, content: { video_url } }
// We submit, poll until succeeded/failed, then fetch the produced
// video_url and return the raw bytes. The temporary URL Volcengine
// returns is only valid for ~24h, so streaming the bytes into the
// project folder is required to keep them addressable.
// ---------------------------------------------------------------------------

async function renderVolcengineVideo(ctx: MediaContext, credentials: ProviderConfig, onProgress?: ProgressFn): Promise<RenderResult> {
  if (!credentials.apiKey) {
    throw new Error(
      'no Volcengine Ark API key — configure it in Settings or set ARK_API_KEY',
    );
  }
  const baseUrl = (credentials.baseUrl || 'https://ark.cn-beijing.volces.com/api/v3').replace(/\/$/, '');

  // Seedance accepts inline `--resolution`, `--duration`, `--ratio` and
  // `--camerafixed` flags inside the prompt text. We append a flags
  // suffix so user prompts that already contain them still win.
  const ratio = volcengineRatioFor(ctx.aspect);
  const durationSec = ctx.length || 5;
  const resolution = '720p';
  const promptText = (ctx.prompt && ctx.prompt.trim()) || 'A short cinematic clip.';
  const suffixFlags: string[] = [];
  if (!/--resolution\b/.test(promptText)) suffixFlags.push(`--resolution ${resolution}`);
  if (!/--duration\b/.test(promptText)) suffixFlags.push(`--duration ${durationSec}`);
  if (!/--ratio\b/.test(promptText)) suffixFlags.push(`--ratio ${ratio}`);
  const fullText = suffixFlags.length
    ? `${promptText} ${suffixFlags.join(' ')}`

View on GitHub (pinned to 5be4028344)

Solutions

  1. Set `ARK_API_KEY` in the daemon's environment (or export it in the shell that launches `tools-dev`), or enter the key under Settings → Providers → Volcengine.
  2. Restart the daemon after adding the key so `resolveProviderConfig` re-reads it.
  3. Confirm the selected model is actually a Volcengine model and not an aliased one routed elsewhere.

Example fix

// before
// (no key configured, Seedance model selected)
od media generate --surface video --model doubao-seedance-1-0-t2v --prompt '...'

// after
export ARK_API_KEY=sk-xxxx   # in the shell that runs tools-dev
od media generate --surface video --model doubao-seedance-1-0-t2v --prompt '...'
Defensive patterns

Strategy: validation

Validate before calling

function ensureVolcengineCreds(credentials: ProviderConfig): void {
  if (!credentials.apiKey) {
    throw new Error('no Volcengine Ark API key — configure it in Settings or set ARK_API_KEY');
  }
}
// call before renderVolcengineVideo(ctx, credentials, onProgress)
ensureVolcengineCreds(credentials);

Type guard

function hasVolcengineCreds(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('volcengine')` resolves the key from Settings, `ARK_API_KEY`, or media-config, while the selected model is a `provider: 'volcengine'` video model (e.g. `doubao-seedance-*`).

Common situations: (1) Fresh install where the user picked a Seedance model before adding a key; (2) `ARK_API_KEY` set in a different shell than the one running the daemon; (3) 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/4321ccb66d1fcdaf. Report an issue: GitHub.