jackwener/OpenCLI · error · ArgumentError

clip-id required (UUID or /song/<id> URL)

Error message

clip-id required (UUID or /song/<id> URL)

What it means

parseClipId() requires a non-empty clip id — either a raw UUID or a https://suno.com/song/<uuid> URL. Empty input (missing positional arg, empty string, null/undefined) throws this ArgumentError before any network call is made.

Source

Thrown at clis/suno/download.js:33

    downloadSunoClip,
    ensureSunoSession,
    normalizeBooleanFlag,
    parseFormats,
    resolveSunoOutputDir,
    unwrapEvaluateResult,
} from './utils.js';

import * as os from 'node:os';

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,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Pass a clip UUID or a suno.com/song/<uuid> URL as the first positional argument
  2. If the id comes from a previous command, verify that step actually emitted an id
  3. Quote shell variables so an empty value doesn't silently disappear

Example fix

// before
const clip = process.env.CLIP_ID; // '' -> ArgumentError
await cli('suno', 'download', clip);
// after
if (!process.env.CLIP_ID) throw new Error('CLIP_ID is not set');
await cli('suno', 'download', process.env.CLIP_ID);
Defensive patterns

Strategy: validation

Validate before calling

function requireClipId(value) {
  const raw = String(value ?? '').trim();
  if (!raw) throw new Error('clip-id required (UUID or /song/<id> URL)');
  return raw;
}
requireClipId(process.argv[2]);

Type guard

function hasClipId(v) { return typeof v === 'string' && v.trim().length > 0; }

Try / catch

try {
  await cli('suno', 'download', clipArg);
} catch (e) {
  if (/clip-id required/.test(e.message)) {
    console.error('Usage: opencli suno download <clip-uuid-or-url>');
    process.exitCode = 2;
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Running `opencli suno download` without the positional clip argument, or passing an empty/whitespace-only value (String(value||'').trim() yields '').

Common situations: Scripting the CLI with a variable that is unset/empty because an earlier pipeline step produced no id; forgetting the positional arg entirely; shell quoting dropping the argument.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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