jackwener/OpenCLI · error · ArgumentError

shot URL must match dribbble.com/shots/<id>

Error message

shot URL must match dribbble.com/shots/<id>

What it means

The URL host is valid dribbble.com, but the pathname does not match /shots/<numeric id>. requireShotTarget requires the path to start with /shots/ followed by digits (optionally followed by '-', '/', or end of path). This ArgumentError is thrown for dribbble.com pages that are not individual shot pages.

Source

Thrown at clis/dribbble/utils.js:63

    return designer;
}

export function requireShotTarget(value) {
    const target = String(value ?? '').trim();
    if (!target) throw new ArgumentError('shot is required (numeric id or dribbble.com/shots URL)');
    if (/^\d+$/.test(target)) return target;

    let url;
    try {
        url = new URL(target);
    } catch {
        throw new ArgumentError('shot must be a numeric id or dribbble.com/shots URL');
    }
    if (!/(^|\.)dribbble\.com$/i.test(url.hostname)) {
        throw new ArgumentError('shot URL must use dribbble.com');
    }
    const match = url.pathname.match(/^\/shots\/(\d+)(?:-|\/|$)/);
    if (!match) throw new ArgumentError('shot URL must match dribbble.com/shots/<id>');
    return match[1];
}

export function requireRows(payload, command) {
    if (!payload || typeof payload !== 'object') {
        throw new CommandExecutionError(`${command} returned an unreadable browser payload`);
    }
    if (payload.empty) {
        throw new EmptyResultError(command, payload.reason || `${command} returned no results`);
    }
    if (!payload.ok) {
        const reason = payload.reason ? `: ${payload.reason}` : '';
        throw new CommandExecutionError(`${command} selector drift${reason}`);
    }
    if (!Array.isArray(payload.rows)) {
        throw new CommandExecutionError(`${command} returned a malformed rows payload`);
    }
    if (payload.rows.length === 0) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Use a URL of the form https://dribbble.com/shots/<numeric id> (trailing slug like /shots/12345-Name is accepted).
  2. If you only have a profile or listing URL, first extract the specific shot id you need and pass that.
  3. Verify the id portion is numeric — alphabetic path segments fail the match.

Example fix

// before
cli.shot({ shot: 'https://dribbble.com/halolab' }) // profile, not a shot

// after
cli.shot({ shot: 'https://dribbble.com/shots/1234567-halolab-shot' });
Defensive patterns

Strategy: validation

Validate before calling

const url = new URL(shot);
const m = url.pathname.match(/^\/shots\/(\d+)(?:-|\/|$)/);
if (!m) throw new Error('shot URL must be dribbble.com/shots/<numeric id>');

Type guard

function isShotPageUrl(v) {
  try { return /^\/shots\/(\d+)(?:-|\/|$)/.test(new URL(String(v)).pathname); } catch { return false; }
}

Try / catch

try {
  const result = command({ shot });
} catch (e) {
  if (e instanceof ArgumentError && /shots\/<id>/.test(e.message)) {
    console.error(`'${shot}' is a dribbble.com page but not a shot page; use /shots/<numeric id>`);
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing URLs like 'https://dribbble.com/halolab' (profile page), 'https://dribbble.com/shots' (listing), 'https://dribbble.com/shots/abc' (non-numeric id), or a shot URL with extra segments the regex doesn't accept.

Common situations: Grabbing a designer profile URL when a shot was intended, copying a shots listing page, or saving a URL with a slug-less/typo'd id from bookmarks.

Related errors


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