nexu-io/open-design · error · Error

volcengine task did not finish in time (last status: ${lastS

Error message

volcengine task did not finish in time (last status: ${lastStatus || 'unknown'})

What it means

Thrown when the polling loop exits without the task reaching `succeeded` within the configured deadline. The default cap is 12 minutes (`12 * 60 * 1000`); `OD_VOLCENGINE_VIDEO_MAX_POLL_MS` overrides it but only values ≥ 60000 are honored. The last observed status is included so you can tell a still-queued task from a stuck one.

Source

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

    // Forward each poll tick. Heartbeat doubles as a "command is alive"
    // signal for the agent's bash tool — the daemon's SSE stream emits
    // an event for every line, which cc renders into the chat as live
    // output so its watchdog never marks the call as hung.
    if (typeof onProgress === 'function') {
      const elapsedSec = Math.round((Date.now() - startedAt) / 1000);
      onProgress(`volcengine task ${taskId} status=${lastStatus || 'pending'} (elapsed ${elapsedSec}s)`);
    }
    if (lastStatus === 'succeeded') {
      videoUrl = pollData?.content?.video_url || null;
      break;
    }
    if (lastStatus === 'failed' || lastStatus === 'cancelled') {
      const reason = pollData?.error?.message || lastStatus;
      throw new Error(`volcengine task ${lastStatus}: ${reason}`);
    }
  }
  if (!videoUrl) {
    throw new Error(`volcengine task did not finish in time (last status: ${lastStatus || 'unknown'})`);
  }

  const dlResp = await fetch(videoUrl, withMediaRequestInit(ctx));
  if (!dlResp.ok) throw new Error(`volcengine video fetch ${dlResp.status}`);
  const arr = await dlResp.arrayBuffer();
  const bytes = Buffer.from(arr);

  return {
    bytes,
    providerNote: `volcengine/${ctx.wireModel} · ${ratio} · ${durationSec}s · ${bytes.length} bytes`,
    suggestedExt: '.mp4',
  };
}

function volcengineRatioFor(aspect?: string): string {
  // Seedance accepts a fixed list of ratios; map the OD vocabulary to
  // its canonical strings.
  if (!aspect) return '16:9';

View on GitHub (pinned to 5be4028344)

Solutions

  1. Increase the cap by setting `OD_VOLCENGINE_VIDEO_MAX_POLL_MS` (e.g. `1800000` for 30 min) in the daemon env; values below 60000 are ignored.
  2. Retry at off-peak; switch region if the account supports it.
  3. Reduce resolution/duration to shorten render time.

Example fix

// before
od media generate --surface video --model doubao-seedance-1-0-i2v --prompt '...'
# → 'volcengine task did not finish in time (last status: running)'

// after
export OD_VOLCENGINE_VIDEO_MAX_POLL_MS=1800000   # 30 min
od media generate --surface video --model doubao-seedance-1-0-i2v --prompt '...'
Defensive patterns

Strategy: retry

Validate before calling

// Bump the poll cap before rendering long i2v tasks.
function ensureVolcengineTimeout(): void {
  const v = Number(process.env.OD_VOLCENGINE_VIDEO_MAX_POLL_MS);
  if (!Number.isFinite(v) || v < 60_000) {
    process.env.OD_VOLCENGINE_VIDEO_MAX_POLL_MS = String(12 * 60 * 1000);
  }
}

Try / catch

// Increase the cap and retry once for genuine long-queue timeouts.
process.env.OD_VOLCENGINE_VIDEO_MAX_POLL_MS = String(30 * 60 * 1000);
return await renderVolcengineVideo(ctx, credentials, onProgress);

Prevention

When it happens

Trigger: `Date.now() - startedAt >= maxMs` before `status === 'succeeded'`. Real Seedance i2v or busy-region t2v can exceed 6 minutes; the 12-minute default covers most, but heavy queues or large resolutions can still time out.

Common situations: (1) Seedance queue saturated in the region; (2) i2v with a large reference image; (3) the default 12-min cap too short for a peak-traffic render.

Related errors


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