jackwener/OpenCLI · error · ArgumentError

Invalid clip-id: ${raw}

Error message

Invalid clip-id: ${raw}

What it means

parseClipId() accepts either a bare UUID (case-insensitive hex pattern) or an exact https://suno.com/song/<uuid> URL. Anything else — malformed UUID, wrong hostname/path, non-https URL, or unparseable string — throws ArgumentError with the offending value and a hint about the accepted formats.

Source

Thrown at clis/suno/download.js:43

function displayPath(filePath) {
    if (!filePath) return '-';
    const home = os.homedir();
    return filePath.startsWith(home) ? `~${filePath.slice(home.length)}` : filePath;
}

function parseClipId(value) {
    const raw = String(value || '').trim();
    if (!raw) throw new ArgumentError('clip-id required (UUID or /song/<id> URL)');
    const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
    if (uuidPattern.test(raw)) return raw.toLowerCase();
    try {
        const parsed = new URL(raw);
        const pathMatch = parsed.pathname.match(/^\/song\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\/?$/i);
        if (parsed.protocol === 'https:' && parsed.hostname === 'suno.com' && pathMatch) {
            return pathMatch[1].toLowerCase();
        }
    } catch {}
    throw new ArgumentError(`Invalid clip-id: ${raw}`, 'Pass a UUID or a https://suno.com/song/<uuid> URL.');
}

export const downloadCommand = cli({
    site: 'suno',
    name: 'download',
    access: 'write',
    description: 'Download an existing Suno clip (MP3 + optional WAV/M4A/video) by id',
    domain: SUNO_DOMAIN,
    strategy: Strategy.COOKIE,
    browser: true,
    siteSession: 'persistent',
    navigateBefore: false,
    defaultFormat: 'plain',
    args: [
        { name: 'clip', positional: true, required: true, help: 'Clip UUID or https://suno.com/song/<id> URL' },
        { name: 'formats', help: 'Comma-separated formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata' },
        { name: 'op', help: 'Output directory (default: ~/Music/suno)' },
        { name: 'confirm-paid', type: 'boolean', default: false, help: 'Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning.' },

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Use the canonical https://suno.com/song/<uuid> URL (strip query strings) or just the UUID
  2. Extract the UUID from a longer URL manually and pass the UUID alone
  3. Note www.suno.com and non-https variants are rejected — use https://suno.com exactly

Example fix

// before
await cli('suno', 'download', 'https://suno.com/song/abc123?utm=x');
// -> ArgumentError: Invalid clip-id
// after
const m = url.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);
await cli('suno', 'download', m[0]);
Defensive patterns

Strategy: validation

Validate before calling

const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
function extractClipId(input) {
  const raw = String(input || '').trim();
  if (UUID.test(raw)) return raw.toLowerCase();
  const m = raw.match(/https:\/\/suno\.com\/song\/([0-9a-f-]{36})/i);
  if (m) return m[1].toLowerCase();
  throw new Error(`Not a valid clip id: ${raw}`);
}

Type guard

function isValidClipId(v) {
  return typeof v === 'string' &&
    (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v.trim()) ||
     /^https:\/\/suno\.com\/song\/[0-9a-f-]{36}\/?$/i.test(v.trim()));
}

Try / catch

try {
  await cli('suno', 'download', clipInput);
} catch (e) {
  if (/Invalid clip-id/.test(e.message)) {
    const uuid = clipInput.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);
    if (uuid) return cli('suno', 'download', uuid[0].toLowerCase());
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing a full song page URL with extra path/query (e.g. ?shuffle=true), an http:// URL, a suno.org or www hostname, a truncated id, or an internal numeric id instead of the UUID.

Common situations: Copying a share link with tracking query params; using the song's slug URL (/song/title-slug) instead of the canonical UUID URL; pasting an embed URL; old bookmark with a different domain.

Related errors


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