{"record":{"id":"8794cacb29a6e090","repo":"jackwener/OpenCLI","slug":"suno-feed-api-returned-malformed-clips-payload","errorCode":null,"errorMessage":"Suno feed API returned malformed clips payload","messagePattern":"Suno feed API returned malformed clips payload","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/suno/utils.js","lineNumber":386,"sourceCode":"        })()`));\n\n        if (!result) {\n            await page.wait(pollSeconds);\n            continue;\n        }\n        if (result.status === 401 || result.status === 403) {\n            throw new AuthRequiredError(SUNO_DOMAIN, `Suno feed API rejected (HTTP ${result.status}). Re-login.`);\n        }\n        if (result.status < 200 || result.status >= 300) {\n            throw new CommandExecutionError(`Suno feed API failed while polling clips (HTTP ${result.status || '?'})`);\n        }\n        if (!result.body || typeof result.body !== 'object' || Array.isArray(result.body)) {\n            throw new CommandExecutionError('Suno feed API returned malformed JSON while polling clips');\n        }\n\n        const allClips = result.body.clips || [];\n        if (!Array.isArray(allClips)) {\n            throw new CommandExecutionError('Suno feed API returned malformed clips payload');\n        }\n        const ourClips = allClips.filter(c => targetSet.has(c.id));\n        const finished = ourClips.filter(c => c.status === 'complete' || c.status === 'error');\n\n        if (typeof onProgress === 'function') {\n            onProgress({ total: clipIds.length, done: finished.length, statuses: ourClips.map(c => `${c.id.slice(0,8)}:${c.status}`) });\n        }\n\n        if (finished.length === clipIds.length) return ourClips;\n        await page.wait(pollSeconds);\n    }\n\n    throw new TimeoutError(`Suno generation did not complete within ${timeoutSeconds}s. Try --timeout <higher>.`);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Asset download.\n// ─────────────────────────────────────────────────────────────────────────────","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/suno/utils.js#L368-L404","documentation":"Even when the feed body is a valid JSON object, pollSunoClips requires body.clips to be an array. If clips is missing or a non-array (object, string, etc.), this CommandExecutionError is thrown before filtering target clip ids.","triggerScenarios":"The feed endpoint returns 2xx JSON whose `clips` field is absent, null, or not an array — a partial API schema change or an error object returned with 200 status.","commonSituations":"Suno rolls out a new feed envelope (e.g. clips nested under data); backend error payloads returned with HTTP 200; region-specific API variants with different shapes.","solutions":["Update the opencli suno CLI to match the current Suno feed schema.","Log result.body keys to discover where the clips array moved (e.g. body.data.clips).","Retry in case a transient backend error object was returned with 200.","Pin/report the issue if Suno is mid-migration and behavior is inconsistent."],"exampleFix":"// before\nconst allClips = result.body.clips || [];\nif (!Array.isArray(allClips)) throw new CommandExecutionError('Suno feed API returned malformed clips payload');\n// after: tolerate nested envelope\nconst raw = result.body.clips ?? result.body.data?.clips ?? [];\nconst allClips = Array.isArray(raw) ? raw : [];","handlingStrategy":"type-guard","validationCode":"// validate clips is an array before filtering\nconst raw = body?.clips ?? body?.data?.clips;\nif (!Array.isArray(raw)) throw new Error('unexpected feed schema: ' + Object.keys(body || {}).join(','));","typeGuard":"function hasClipsArray(body) {\n  return Array.isArray(body?.clips) || Array.isArray(body?.data?.clips);\n}","tryCatchPattern":"try {\n  const clips = await pollSunoClips(page, ids, timeout, deviceId);\n} catch (e) {\n  if (e instanceof CommandExecutionError && /malformed clips payload/.test(e.message)) {\n    // schema drift: update CLI or fall back to manual feed inspection\n  }\n  throw e;\n}","preventionTips":["Keep the Suno CLI updated against feed schema changes","Log body keys when the shape looks wrong","Pin a known-good CLI version in CI"],"tags":["api","schema-change","suno","response-shape"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}