jackwener/OpenCLI · error · CommandExecutionError

Suno feed API returned malformed JSON while polling clips

Error message

Suno feed API returned malformed JSON while polling clips

What it means

pollSunoClips expects /api/feed/v3 to return a JSON object. If res.json() fails (body null), or the parsed body is an array/primitive, this CommandExecutionError is thrown because the poll loop cannot read body.clips.

Source

Thrown at clis/suno/utils.js:381

                headers: ${sunoHeadersJs(deviceId, { 'Content-Type': 'application/json' })},
                body: JSON.stringify({ clip_ids: ${idsJson} }),
            });
            const body = await res.json().catch(() => null);
            return { status: res.status, body };
        })()`));

        if (!result) {
            await page.wait(pollSeconds);
            continue;
        }
        if (result.status === 401 || result.status === 403) {
            throw new AuthRequiredError(SUNO_DOMAIN, `Suno feed API rejected (HTTP ${result.status}). Re-login.`);
        }
        if (result.status < 200 || result.status >= 300) {
            throw new CommandExecutionError(`Suno feed API failed while polling clips (HTTP ${result.status || '?'})`);
        }
        if (!result.body || typeof result.body !== 'object' || Array.isArray(result.body)) {
            throw new CommandExecutionError('Suno feed API returned malformed JSON while polling clips');
        }

        const allClips = result.body.clips || [];
        if (!Array.isArray(allClips)) {
            throw new CommandExecutionError('Suno feed API returned malformed clips payload');
        }
        const ourClips = allClips.filter(c => targetSet.has(c.id));
        const finished = ourClips.filter(c => c.status === 'complete' || c.status === 'error');

        if (typeof onProgress === 'function') {
            onProgress({ total: clipIds.length, done: finished.length, statuses: ourClips.map(c => `${c.id.slice(0,8)}:${c.status}`) });
        }

        if (finished.length === clipIds.length) return ourClips;
        await page.wait(pollSeconds);
    }

    throw new TimeoutError(`Suno generation did not complete within ${timeoutSeconds}s. Try --timeout <higher>.`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run after a pause — challenge pages and truncation are often transient.
  2. Re-login to Suno to clear anti-bot/edge states, then retry.
  3. Inspect the raw response (add logging of res.text()) to identify what was returned.
  4. Update the CLI if Suno changed the /api/feed/v3 response format.

Example fix

// before
const body = await res.json().catch(() => null);
// after: capture raw text for diagnostics when JSON parse fails
const text = await res.text();
let body = null;
try { body = JSON.parse(text); } catch {}
return { status: res.status, body, raw: body ? null : text.slice(0, 300) };
Defensive patterns

Strategy: type-guard

Validate before calling

// after fetch, check parseability before consuming
const text = await res.text();
let body = null; try { body = JSON.parse(text); } catch {}
if (!body || typeof body !== 'object' || Array.isArray(body)) throw new Error('feed returned non-JSON body: ' + text.slice(0, 200));

Type guard

function isFeedBody(b) {
  return !!b && typeof b === 'object' && !Array.isArray(b);
}

Try / catch

try {
  const clips = await pollSunoClips(page, ids, timeout, deviceId);
} catch (e) {
  if (e instanceof CommandExecutionError && /malformed JSON/.test(e.message)) {
    // likely a challenge/maintenance page; pause and retry
    await sleep(20000);
    return pollSunoClips(page, ids, timeout, deviceId);
  }
  throw e;
}

Prevention

When it happens

Trigger: The feed endpoint returns 2xx with a non-JSON body (HTML error page, empty body), so res.json().catch(() => null) yields null, or the body parses to a non-object value.

Common situations: Suno serving a maintenance/Cloudflare challenge page with 2xx; truncated response; API version drift on /api/feed/v3; anti-bot interstitials when the session looks suspicious.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/53cb4e931c8f6aaa. Report an issue: GitHub.