jackwener/OpenCLI · error · ArgumentError

"${raw}" is a Pinterest /${username}/ URL, not a board

Error message

"${raw}" is a Pinterest /${username}/ URL, not a board

What it means

tryParseBoardRef rejects Pinterest URLs whose first path segment is a reserved root such as /pin/ rather than a username. Those URLs point at pins or other Pinterest resources, not boards, so throwing prevents silently misinterpreting the reference. The hint for /pin/ URLs points users at `opencli pinterest pin <id>` to discover the owning board.

Source

Thrown at clis/pinterest/utils.js:82

 * Returns null for anything else (e.g. a bare board id, which needs an API lookup).
 */
export function tryParseBoardRef(raw) {
  const trimmed = String(raw ?? '').trim();
  if (!trimmed) return null;
  let pathname = trimmed;
  if (/^https?:\/\//i.test(trimmed)) {
    try {
      pathname = new URL(trimmed).pathname;
    } catch {
      throw new ArgumentError(`Invalid board URL: ${trimmed}`, 'Use a full board URL like https://www.pinterest.com/janedoe/my-board/');
    }
  }
  // Pinterest percent-encodes non-ASCII slugs in the URLs it hands out; the API wants them decoded.
  const parts = pathname.split('/').filter(Boolean).map(decodeSegment);
  if (parts.length < 2) return null;
  const [username, slug] = parts;
  if (RESERVED_PATH_ROOTS.has(username.toLowerCase())) {
    throw new ArgumentError(
      `"${raw}" is a Pinterest /${username}/ URL, not a board`,
      username.toLowerCase() === 'pin'
        ? 'Pass the board this pin lives on, e.g. janedoe/my-board (`opencli pinterest pin <id>` reports it)'
        : 'Pass <username>/<slug>, a board URL, or a numeric board id',
    );
  }
  return { username, slug, path: `/${username}/${slug}/` };
}

/** Normalize a username or profile URL to a bare username (Pinterest has no @-handles). */
export function parseUsername(raw) {
  let value = String(raw ?? '').trim();
  if (!value) throw new ArgumentError('username is required', 'Pass a Pinterest username or profile URL, e.g. janedoe');
  if (/^https?:\/\//i.test(value)) {
    try {
      value = decodeSegment(new URL(value).pathname.split('/').filter(Boolean)[0] || '');
    } catch {
      throw new ArgumentError(`Invalid profile URL: ${raw}`, 'Use a full profile URL like https://www.pinterest.com/janedoe/');

View on GitHub (pinned to 49907e53dc)

Solutions

  1. If it's a /pin/ URL, run `opencli pinterest pin <id>` to find the board, then pass that board
  2. Pass the board as username/slug, e.g. janedoe/my-board
  3. Pass the board's canonical URL https://www.pinterest.com/<user>/<slug>/
  4. Pass the numeric board id instead of a resource URL

Example fix

// before
await direct('https://www.pinterest.com/pin/1234567890123456/');
// after
await direct('janedoe/my-board'); // board the pin lives on
Defensive patterns

Strategy: validation

Validate before calling

const RESERVED = new Set(['pin', 'search', 'today', 'ideas', 'resource']);
function looksLikeBoardUrl(s) {
  if (!/^https?:\/\//i.test(s || '')) return true;
  const seg = new URL(s).pathname.split('/').filter(Boolean)[0];
  return seg ? !RESERVED.has(seg.toLowerCase()) : false;
}

Type guard

const isPinUrl = (v) => typeof v === 'string' && /(^|\/)pin\/\d+/.test(v);

Try / catch

try {
  ref = tryParseBoardRef(input);
} catch (err) {
  if (err instanceof ArgumentError && /not a board/.test(err.message)) {
    console.error(`${err.message}\n${err.hint}`); // hint says to use `pinterest pin <id>`
    process.exitCode = 2;
  } else throw err;
}

Prevention

When it happens

Trigger: Passing https://www.pinterest.com/pin/1234567890123456/ (or another reserved root like /search/, /today/) to a board-parsing command; the first path segment lowercased is in RESERVED_PATH_ROOTS.

Common situations: Copying a pin URL from the browser when you meant the board URL; sharing links from an email that point at pins; automation that always pastes the current page URL.

Related errors


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