{"record":{"id":"9304a7f25c0ead33","repo":"jackwener/OpenCLI","slug":"suno-feed-lookup-returned-malformed-clips-payload","errorCode":null,"errorMessage":"Suno feed lookup returned malformed clips payload","messagePattern":"Suno feed lookup returned malformed clips payload","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/suno/download.js","lineNumber":108,"sourceCode":"                headers: {\n                    'Authorization': 'Bearer ' + (await window.Clerk.session.getToken()),\n                    'browser-token': browserToken,\n                    'device-id': ${JSON.stringify(deviceId)},\n                    'Content-Type': 'application/json',\n                },\n                body: JSON.stringify({ clip_ids: ['${clipId}'] }),\n            });\n            if (!res.ok) return { ok: false, error: 'HTTP ' + res.status };\n            const payload = await res.json().catch(() => null);\n            if (!payload || !Array.isArray(payload.clips)) return { ok: false, error: 'malformed clips payload' };\n            return { ok: true, clips: payload.clips };\n        })()`));\n\n        if (!feedRes?.ok) {\n            throw new CommandExecutionError(`Suno feed lookup failed: ${feedRes?.error || 'unknown'}`);\n        }\n        if (!Array.isArray(feedRes.clips)) {\n            throw new CommandExecutionError('Suno feed lookup returned malformed clips payload');\n        }\n        const clip = feedRes.clips.find(c => c.id === clipId);\n        if (!clip) {\n            throw new EmptyResultError('suno download', `Clip ${clipId} not found in your account. Confirm at ${SUNO_URL}/song/${clipId}.`);\n        }\n        if (clip.status !== 'complete') {\n            throw new CommandExecutionError(`Clip ${clipId} status is \"${clip.status}\" — not complete yet. Retry once generation finishes.`);\n        }\n\n        const result = await downloadSunoClip(page, clip, outputDir, formats, deviceId);\n        if (!result.written.some(w => w.ok)) {\n            throw new CommandExecutionError(`Suno download wrote no files for clip ${clipId}`);\n        }\n        const link = `${SUNO_URL}/song/${clip.id}`;\n        const writtenSummary = result.written\n            .map(w => w.ok ? `${w.format}:${displayPath(w.file)}` : `${w.format}:✗(${w.reason})`)\n            .join(' | ');\n        const skippedSummary = skippedPaid.length","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/suno/download.js#L90-L126","documentation":"`opencli suno download` fetches the user's Suno feed via an in-page API call and expects `feedRes.clips` to be an array so it can find the requested clip. This CommandExecutionError is thrown when the feed endpoint responds ok but the `clips` field is missing, null, or not an array — i.e. Suno's payload shape changed or the response is not a feed payload. It is a defensive guard against silently calling `.find()` on undefined.","triggerScenarios":"Running `opencli suno download <clip-id>` when the Suno feed API returns HTTP ok but a body where `clips` is absent/not an array — e.g. Suno changed the feed response schema, an auth/degraded response returns a JSON object without `clips`, or the page-eval bridge unwraps an unexpected object.","commonSituations":"Suno frontend/API version drift (site updated response shape), expired or partially-valid session cookies yielding a soft-error JSON body with HTTP 200, rate-limited or region-blocked responses that still return ok, or proxy/CDN interstitials returning non-feed JSON.","solutions":["Re-run the command; transient/degraded feed responses often resolve on retry.","Refresh the Suno session: open https://suno.com in Chrome, confirm you are logged in, then retry the command.","Check for an updated opencli version that tracks the current Suno feed API schema (npm update / reinstall @jackwener/opencli).","Inspect the raw feed response manually (browser devtools on suno.com feed endpoint) to confirm the schema and file an issue if it diverged."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// after invoking the feed lookup, before using clips\nif (!feedRes?.ok) throw new Error('feed lookup failed');\nif (!Array.isArray(feedRes.clips)) throw new Error('malformed clips payload');","typeGuard":"function hasClips(res) {\n  return res != null && typeof res === 'object' && Array.isArray(res.clips);\n}","tryCatchPattern":"try {\n  await run('opencli suno download <id>');\n} catch (err) {\n  if (String(err.message).includes('malformed clips payload')) {\n    // retry once, then refresh session / report schema drift\n  } else throw err;\n}","preventionTips":["Keep the CLI updated so it tracks Suno's current feed response schema.","Retry transiently before assuming a hard failure.","Re-login to suno.com in the driven Chrome profile when payloads look degraded."],"tags":["suno","api-schema","network","response-validation"],"backgroundTag":"malformed-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}