jackwener/OpenCLI · error · CommandExecutionError

Suno feed lookup returned malformed clips payload

Error message

Suno feed lookup returned malformed clips payload

What it means

`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.

Source

Thrown at clis/suno/download.js:108

                headers: {
                    'Authorization': 'Bearer ' + (await window.Clerk.session.getToken()),
                    'browser-token': browserToken,
                    'device-id': ${JSON.stringify(deviceId)},
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ clip_ids: ['${clipId}'] }),
            });
            if (!res.ok) return { ok: false, error: 'HTTP ' + res.status };
            const payload = await res.json().catch(() => null);
            if (!payload || !Array.isArray(payload.clips)) return { ok: false, error: 'malformed clips payload' };
            return { ok: true, clips: payload.clips };
        })()`));

        if (!feedRes?.ok) {
            throw new CommandExecutionError(`Suno feed lookup failed: ${feedRes?.error || 'unknown'}`);
        }
        if (!Array.isArray(feedRes.clips)) {
            throw new CommandExecutionError('Suno feed lookup returned malformed clips payload');
        }
        const clip = feedRes.clips.find(c => c.id === clipId);
        if (!clip) {
            throw new EmptyResultError('suno download', `Clip ${clipId} not found in your account. Confirm at ${SUNO_URL}/song/${clipId}.`);
        }
        if (clip.status !== 'complete') {
            throw new CommandExecutionError(`Clip ${clipId} status is "${clip.status}" — not complete yet. Retry once generation finishes.`);
        }

        const result = await downloadSunoClip(page, clip, outputDir, formats, deviceId);
        if (!result.written.some(w => w.ok)) {
            throw new CommandExecutionError(`Suno download wrote no files for clip ${clipId}`);
        }
        const link = `${SUNO_URL}/song/${clip.id}`;
        const writtenSummary = result.written
            .map(w => w.ok ? `${w.format}:${displayPath(w.file)}` : `${w.format}:✗(${w.reason})`)
            .join(' | ');
        const skippedSummary = skippedPaid.length

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command; transient/degraded feed responses often resolve on retry.
  2. Refresh the Suno session: open https://suno.com in Chrome, confirm you are logged in, then retry the command.
  3. Check for an updated opencli version that tracks the current Suno feed API schema (npm update / reinstall @jackwener/opencli).
  4. Inspect the raw feed response manually (browser devtools on suno.com feed endpoint) to confirm the schema and file an issue if it diverged.
Defensive patterns

Strategy: type-guard

Validate before calling

// after invoking the feed lookup, before using clips
if (!feedRes?.ok) throw new Error('feed lookup failed');
if (!Array.isArray(feedRes.clips)) throw new Error('malformed clips payload');

Type guard

function hasClips(res) {
  return res != null && typeof res === 'object' && Array.isArray(res.clips);
}

Try / catch

try {
  await run('opencli suno download <id>');
} catch (err) {
  if (String(err.message).includes('malformed clips payload')) {
    // retry once, then refresh session / report schema drift
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/9304a7f25c0ead33. Report an issue: GitHub.