{"record":{"id":"77cb6f49aa3fb7cd","repo":"jackwener/OpenCLI","slug":"suno-feed-lookup-returned-malformed-clip-identity","errorCode":null,"errorMessage":"Suno feed lookup returned malformed clip identity","messagePattern":"Suno feed lookup returned malformed clip identity","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/suno/list.js","lineNumber":67,"sourceCode":"            if (!res.ok) return { ok: false, status: res.status };\n            const data = await res.json().catch(() => null);\n            if (!data || !Array.isArray(data.clips)) return { ok: false, error: 'malformed clips payload' };\n            return { ok: true, clips: data.clips };\n        })()`));\n\n        if (!result?.ok) {\n            throw new CommandExecutionError(result?.error || `Suno feed lookup failed (HTTP ${result?.status || '?'}).`);\n        }\n        if (!Array.isArray(result.clips)) {\n            throw new CommandExecutionError('Suno feed lookup returned malformed clips payload');\n        }\n        if (result.clips.length === 0) {\n            throw new EmptyResultError('suno list', 'No Suno clips found in your library.');\n        }\n\n        return result.clips.slice(0, limit).map((c, i) => {\n            if (!c || typeof c.id !== 'string' || !c.id) {\n                throw new CommandExecutionError('Suno feed lookup returned malformed clip identity');\n            }\n            return {\n                rank: i + 1 + pageOffset * limit,\n                clip: c.id.slice(0, 8),\n                title: c.title || '(untitled)',\n                status: c.status || '?',\n                created: (c.created_at || '').replace('T', ' ').replace(/\\..*$/, '').replace(/Z$/, ''),\n                link: `${SUNO_URL}/song/${c.id}`,\n            };\n        });\n    },\n});\n","sourceCodeStart":49,"sourceCodeEnd":80,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/suno/list.js#L49-L80","documentation":"The `suno list` command fetches your Suno feed from the studio API and maps each clip to a table row. A clip entry that is null/falsy or lacks a non-empty string `id` cannot be rendered (no clip key, no song link), so the command throws CommandExecutionError('Suno feed lookup returned malformed clip identity') rather than emitting a broken row. This guards against schema drift in Suno's /api/feed/v2 payload.","triggerScenarios":"Calling `opencli suno list` when the /api/feed/v2 response contains an entry in `clips` that is null/undefined, or whose `id` field is missing, non-string (e.g. number), or an empty string. This can happen on partially-published or deleted clips still present in the feed, or if Suno changes the feed schema (e.g. renames `id`).","commonSituations":"Suno API schema changes after the CLI was written; a clip that was deleted mid-feed pagination; A/B-tested feed responses returning placeholder objects; using an outdated CLI version against the current suno.com web API.","solutions":["Update the opencli package to the latest version in case Suno changed the /api/feed/v2 schema and the CLI was patched to match.","Retry `suno list` on a different page (`--page-offset`) or after a few minutes to skip the transient/deleted clip.","Inspect the raw payload (e.g. fetch the same endpoint in a Chrome tab signed into suno.com) to see which clip entry lacks an `id`.","As a workaround, reduce `--limit` so the malformed entry falls outside the sliced window, if it appears late in the feed."],"exampleFix":"// before (client code unaffected; guard is in the CLI)\nconst clip = clips.find(c => c && typeof c.id === 'string' && c.id);\n// after (defensive consume of feed data in your own code)\nconst rows = (result.clips || []).filter(c => c && typeof c.id === 'string' && c.id).map(c => ({ clip: c.id.slice(0,8), title: c.title || '(untitled)' }));","handlingStrategy":"type-guard","validationCode":"const clips = Array.isArray(result.clips) ? result.clips : [];\nconst usable = clips.filter(c => c && typeof c.id === 'string' && c.id.length > 0);\nif (usable.length === 0) throw new Error('No valid clips in feed');","typeGuard":"function isValidClip(c) {\n  return !!c && typeof c === 'object' && typeof c.id === 'string' && c.id.length > 0;\n}","tryCatchPattern":"try {\n  const rows = await sunoList();\n} catch (err) {\n  if (err.message.includes('malformed clip identity')) {\n    // degrade gracefully: filter/skip bad entries or retry with a lower limit\n  } else throw err;\n}","preventionTips":["Keep the CLI updated against Suno feed schema changes.","Filter feed entries through an id type guard before rendering.","Handle EmptyResultError separately so partial/malformed payloads are distinguishable from empty libraries."],"tags":["suno","api-schema","cli","data-validation"],"backgroundTag":"api-response-schema-mismatch","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}