nexu-io/open-design · error · Error

volcengine non-JSON: ${truncate(taskText, 200)}

Error message

volcengine non-JSON: ${truncate(taskText, 200)}

What it means

Thrown when `JSON.parse(taskText)` throws on the task-creation response. Volcengine (or an intermediary) returned a 2xx body that isn't JSON — typically an HTML error/gateway page — so the dispatcher surfaces up to 200 chars for diagnosis instead of an opaque parse error.

Source

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

  };

  const taskResp = await fetch(`${baseUrl}/contents/generations/tasks`, withMediaRequestInit(ctx, {
    method: 'POST',
    headers: {
      'authorization': `Bearer ${credentials.apiKey}`,
      'content-type': 'application/json',
    },
    body: JSON.stringify(taskBody),
  }));
  const taskText = await taskResp.text();
  if (!taskResp.ok) {
    throw new Error(`volcengine task create ${taskResp.status}: ${truncate(taskText, 240)}`);
  }
  let taskData: any;
  try {
    taskData = JSON.parse(taskText);
  } catch {
    throw new Error(`volcengine non-JSON: ${truncate(taskText, 200)}`);
  }
  const taskId = taskData && taskData.id;
  if (!taskId) throw new Error('volcengine task response missing id');

  // Poll until succeeded/failed. Keep a hard cap, but make it long
  // enough for real Seedance queues: fast t2v often returns in 30-120s,
  // while i2v and busy-region t2v can exceed the old 6-minute ceiling.
  const startedAt = Date.now();
  const configuredMaxMs = Number(process.env.OD_VOLCENGINE_VIDEO_MAX_POLL_MS);
  const maxMs =
    Number.isFinite(configuredMaxMs) && configuredMaxMs >= 60_000
      ? configuredMaxMs
      : 12 * 60 * 1000;
  let videoUrl: string | null = null;
  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

View on GitHub (pinned to 5be4028344)

Solutions

  1. Inspect the truncated body — HTML (`<html`/`<title>`) points to a gateway/proxy; fix routing or wait out the outage.
  2. Verify `credentials.baseUrl` (or unset it to use the default `ark.cn-beijing.volces.com`).
  3. If on a restricted network, ensure the Ark host is reachable and not redirected.
Defensive patterns

Strategy: try-catch

Validate before calling

// Reject HTML/gateway bodies before JSON.parse ever runs.
function looksLikeArkJson(text: string): boolean {
  const head = text.slice(0, 100).trim();
  return head.startsWith('{') || head.startsWith('[');
}
if (!looksLikeArkJson(taskText)) {
  throw new Error(`volcengine non-JSON (not an Ark body): ${truncate(taskText, 200)}`);
}

Try / catch

try { taskData = JSON.parse(taskText); }
catch { throw new Error(`volcengine non-JSON: ${truncate(taskText, 200)}`); }

Prevention

When it happens

Trigger: 2xx task-creation response that fails `JSON.parse`. Causes: a captive-portal/HTML auth wall, a Volcengine gateway returning an HTML 502/504 maintenance page with a 2xx status, or a `baseUrl` override pointing at a non-Ark host that serves HTML.

Common situations: (1) Behind a captive portal or corporate proxy injecting an HTML interstitial; (2) Volcengine regional outage surfacing an HTML status page with a 2xx status; (3) `baseUrl` mistyped to a path that serves HTML.

Related errors


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