{"record":{"id":"37f6fb63c47fb382","repo":"jackwener/OpenCLI","slug":"submission-accepted-but-suno-returned-no-clip-ids","errorCode":null,"errorMessage":"Submission accepted but Suno returned no clip ids. Raw: ${JSON.stringify(result.body).slice(0, 300)}","messagePattern":"Submission accepted but Suno returned no clip ids\\. Raw: (.+?)","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"error","filePath":"clis/suno/utils.js","lineNumber":345,"sourceCode":"\n    if (!result || !result.ok) {\n        const status = result?.status || 'unknown';\n        const detail = result?.body?.detail || result?.raw || JSON.stringify(result?.body || {}).slice(0, 500);\n        if (status === 401 || status === 403) {\n            throw new AuthRequiredError(SUNO_DOMAIN, `Suno API rejected request (HTTP ${status}). Re-login on suno.com.`);\n        }\n        if (status === 402) {\n            throw new CommandExecutionError(`Suno API: insufficient credits (HTTP 402). ${detail}`);\n        }\n        throw new CommandExecutionError(`Suno generate failed (HTTP ${status}): ${detail}`);\n    }\n\n    if (!result.body || typeof result.body !== 'object' || Array.isArray(result.body)) {\n        throw new CommandExecutionError('Suno generate returned malformed JSON payload.');\n    }\n    const clips = result.body?.clips || [];\n    if (!clips.length) {\n        throw new EmptyResultError('suno generate', `Submission accepted but Suno returned no clip ids. Raw: ${JSON.stringify(result.body).slice(0, 300)}`);\n    }\n    return result.body;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Poll /api/feed/v3 (cookie auth, no Bearer).\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport async function pollSunoClips(page, clipIds, timeoutSeconds, deviceId, pollSeconds = 5, onProgress = null) {\n    const deadline = Date.now() + timeoutSeconds * 1000;\n    const targetSet = new Set(clipIds);\n    const idsJson = JSON.stringify(clipIds);\n\n    while (Date.now() < deadline) {\n        const result = unwrapEvaluateResult(await page.evaluate(`(async () => {\n            const res = await fetch('${STUDIO_API}/api/feed/v3', {\n                method: 'POST',\n                headers: ${sunoHeadersJs(deviceId, { 'Content-Type': 'application/json' })},","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/suno/utils.js#L327-L363","documentation":"After a 2xx generate response with a valid JSON object, submitSunoGeneration requires a non-empty clips array to extract clip ids. If the object has no clips (or an empty array), EmptyResultError is thrown with a 300-char dump of the raw body for debugging.","triggerScenarios":"Suno accepted the generation POST (2xx) but the returned body.clips is missing or empty — e.g. the request was silently filtered (flagged prompt/lyrics), quota/throttling accepted the job but returned no clips, or the API envelope changed.","commonSituations":"Prompts that trigger Suno's content moderation get accepted but return zero clips; account with exhausted credits edge-cases; Suno A/B testing a new response schema; submitting with malformed style fields the server ignores.","solutions":["Inspect the Raw JSON in the error message to see what Suno actually returned.","Rephrase the prompt/lyrics — content moderation often accepts the request but returns no clips.","Check credits/subscription status on suno.com and retry.","Verify clip ids are read from the right field; update the CLI if Suno renamed the clips field."],"exampleFix":"// before\nconst clips = result.body?.clips || [];\nif (!clips.length) throw new EmptyResultError('suno generate', ...);\n// after: log full body for diagnosis and check alternate fields\nconst clips = result.body?.clips || result.body?.data?.clips || [];\nif (!clips.length) {\n  console.error('Suno body:', JSON.stringify(result.body).slice(0, 500));\n  throw new EmptyResultError('suno generate', 'no clips returned');\n}","handlingStrategy":"validation","validationCode":"// after submit, validate clips before consuming\nif (!body?.clips || !body.clips.length) {\n  console.error('Suno returned:', JSON.stringify(body).slice(0, 300));\n}","typeGuard":"function hasClips(body) {\n  return Array.isArray(body?.clips) && body.clips.length > 0;\n}","tryCatchPattern":"try {\n  const body = await submitSunoGeneration(page, payload);\n} catch (e) {\n  if (e instanceof EmptyResultError) {\n    // inspect e.message raw dump; retry with adjusted prompt (moderation often causes empty clips)\n  } else throw e;\n}","preventionTips":["Avoid prompts likely to trigger content moderation","Check credits/subscription before large batches","Inspect the Raw JSON in the error message before retrying"],"tags":["api","empty-result","suno","response-shape"],"backgroundTag":"empty-response-payload","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}