jackwener/OpenCLI · error · CommandExecutionError

Suno feed API failed while polling clips (HTTP ${result.stat

Error message

Suno feed API failed while polling clips (HTTP ${result.status || '?'})

What it means

pollSunoClips throws this CommandExecutionError when /api/feed/v3 returns any non-2xx, non-401/403 HTTP status (e.g. 429, 5xx). The `|| '?'` fallback covers a missing status from a null-ish evaluate result.

Source

Thrown at clis/suno/utils.js:378

        const result = unwrapEvaluateResult(await page.evaluate(`(async () => {
            const res = await fetch('${STUDIO_API}/api/feed/v3', {
                method: 'POST',
                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);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the command after a short wait — most 5xx/429 are transient.
  2. Increase pollSeconds (e.g. from 5 to 10-15) to reduce request rate.
  3. Check Suno status/Discord for ongoing outages if 5xx persists.
  4. Add retry-with-backoff around pollSunoClips for intermittent 429/5xx.

Example fix

// before
await pollSunoClips(page, ids, 300, deviceId, 3);
// after: slower polling to avoid rate limits
await pollSunoClips(page, ids, 300, deviceId, 10);
Defensive patterns

Strategy: retry

Try / catch

try {
  const clips = await pollSunoClips(page, ids, timeout, deviceId, 10);
} catch (e) {
  if (e instanceof CommandExecutionError && /HTTP \d+/.test(e.message)) {
    // transient 429/5xx: back off and retry
    await sleep(15000);
    return pollSunoClips(page, ids, timeout, deviceId, 15);
  }
  throw e;
}

Prevention

When it happens

Trigger: The feed polling request gets a 429 rate-limit, 500/502/503 server error, or any other unexpected status outside 200-299 while waiting for clips to finish.

Common situations: Polling too aggressively (small pollSeconds) triggering Suno rate limits; Suno incident/outage causing 5xx responses; network proxy intercepting requests; heavy load during peak generation times.

Related errors


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