jackwener/OpenCLI · error · TimeoutError
Suno generation did not complete within ${timeoutSeconds}s.
Error message
Suno generation did not complete within ${timeoutSeconds}s. Try --timeout <higher>. What it means
pollSunoClips loops until every requested clip id reaches status 'complete' or 'error', or the timeout deadline passes. If the deadline expires first, TimeoutError is thrown, suggesting a higher --timeout.
Source
Thrown at clis/suno/utils.js:399
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>.`);
}
// ─────────────────────────────────────────────────────────────────────────────
// Asset download.
// ─────────────────────────────────────────────────────────────────────────────
function pickMediaUrl(clip, contentTypeFragment) {
const arr = Array.isArray(clip.media_urls) ? clip.media_urls : [];
const hit = arr.find(m => (m.content_type || '').toLowerCase().includes(contentTypeFragment));
return hit?.url || null;
}
/**
* Resolve the canonical MP3 URL for a clip. Suno's /api/download/clip/{id}
* returns a fresh signed URL; clip.audio_url is a backup.
*/
async function resolveMp3Url(page, clip, deviceId) {
const fromApi = await page.evaluate(`(async () => {View on GitHub (pinned to 49907e53dc)
Solutions
- Re-run with a higher --timeout (e.g. --timeout 600).
- Check suno.com library page — the clip may have completed after the CLI gave up.
- Increase pollSeconds slightly and retry during off-peak hours if the queue is congested.
- Verify the clip ids are valid; ids absent from the feed will never satisfy the completion check.
Example fix
// before await pollSunoClips(page, ids, 120, deviceId); // after await pollSunoClips(page, ids, 600, deviceId); // or CLI: --timeout 600
Defensive patterns
Strategy: fallback
Try / catch
try {
const clips = await pollSunoClips(page, ids, 300, deviceId);
} catch (e) {
if (e instanceof TimeoutError) {
// fall back to checking the clips later or with a longer timeout
console.warn('Generation still pending; retry with --timeout 600 or check suno.com library');
} else throw e;
} Prevention
- Set --timeout generously (>= 300s) for batch or long generations
- Expect longer queues at peak hours; raise timeout accordingly
- Verify clip ids are valid before polling
When it happens
Trigger: Within timeoutSeconds, at least one requested clip never reports complete/error via /api/feed/v3 — generation still queued/processing, a clip stuck in a nonterminal status, or the clip id never appearing in the feed.
Common situations: Long songs or high queue load during peak hours; very low --timeout (default too short for batch requests); Suno outage leaving generations pending indefinitely; invalid clip ids that never show in feed.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Midjourney job ${jobId} (last status: ${lastStatus})
- Clip ${clipId} status is "${clip.status}" — not complete yet
- Timeout waiting for Antigravity reply after ${timeout / 1000
- Bilibili relation modify did not verify ${expectedLabel}; la
- chatgpt detail
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/680f30c4de8bc9e5.
Report an issue: GitHub.