nexu-io/open-design · error · Error

volcengine poll ${pollResp.status}: ${truncate(pollText, 240

Error message

volcengine poll ${pollResp.status}: ${truncate(pollText, 240)}

What it means

Thrown when polling `GET /api/v3/contents/generations/tasks/{id}` returns a non-2xx status. Unlike creation failures, a poll failure is often transient (rate-limited polling, momentary 5xx), so the status+body are surfaced for triage.

Source

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

  let lastStatus = '';
  // Emit a "task accepted" line right away so the agent's chat shows
  // something within the first second instead of going silent for the
  // full poll loop. cc's Bash tool considers a long-quiet pipe stuck
  // and times out at ~2 minutes — Volcengine i2v routinely takes
  // 3-5 minutes, so without this stream, every i2v dispatch dies
  // mid-flight.
  if (typeof onProgress === 'function') {
    const mode = ctx.imageRef ? 'i2v' : 't2v';
    onProgress(`volcengine ${mode} task ${taskId} accepted; polling status…`);
  }
  while (Date.now() - startedAt < maxMs) {
    await sleep(4000);
    const pollResp = await fetch(`${baseUrl}/contents/generations/tasks/${encodeURIComponent(taskId)}`, withMediaRequestInit(ctx, {
      headers: { 'authorization': `Bearer ${credentials.apiKey}` },
    }));
    const pollText = await pollResp.text();
    if (!pollResp.ok) {
      throw new Error(`volcengine poll ${pollResp.status}: ${truncate(pollText, 240)}`);
    }
    let pollData: any;
    try {
      pollData = JSON.parse(pollText);
    } catch {
      throw new Error(`volcengine poll non-JSON: ${truncate(pollText, 200)}`);
    }
    lastStatus = pollData.status || '';
    // 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;

View on GitHub (pinned to 5be4028344)

Solutions

  1. Retry the whole render — transient poll 5xx/429 usually clear on the next attempt.
  2. If consistently 429, the 4s poll interval may exceed the tier's allowance; reduce concurrency.
  3. On 401/404, restart with a fresh key — the task id is no longer usable.
Defensive patterns

Strategy: retry

Try / catch

// Poll failures are usually transient — retry the whole render once.
try {
  return await renderVolcengineVideo(ctx, credentials, onProgress);
} catch (err) {
  const msg = err instanceof Error ? err.message : String(err);
  if (/poll 5\d{2}|poll 429/.test(msg)) { return await renderVolcengineVideo(ctx, credentials, onProgress); }
  throw err;
}

Prevention

When it happens

Trigger: `pollResp.ok === false` during the 4-second-interval poll loop. Causes: 429 from polling too aggressively for the account tier, 401 from a key invalidated mid-render, 404 if the task id expired, or transient 5xx from the Ark service.

Common situations: (1) Polling rate limit on lower ARK tiers; (2) long-running i2v task outliving key validity; (3) transient Volcengine 5xx mid-render.

Related errors


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