{"record":{"id":"60367bc051f09aaa","repo":"jackwener/OpenCLI","slug":"pin-id-has-no-downloadable-image-it-may-be-a-v","errorCode":null,"errorMessage":"Pin ${id} has no downloadable image (it may be a video or story pin)","messagePattern":"Pin (.+?) has no downloadable image \\(it may be a video or story pin\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/pinterest/download.js","lineNumber":40,"sourceCode":"  func: async (page, kwargs) => {\n    const id = parsePinId(kwargs.pin);\n    const output = String(kwargs.output ?? './pinterest-downloads');\n\n    const sourceUrl = `/pin/${id}/`;\n    await page.goto(`${PINTEREST_BASE}${sourceUrl}`);\n\n    const { data: pin } = await pinterestResourceFetch(\n      page,\n      'PinResource',\n      { id, field_set_key: 'detailed' },\n      sourceUrl,\n    );\n    if (!pin || !pin.id) {\n      throw new EmptyResultError('pinterest download', `pin \"${id}\" not found`);\n    }\n    const imageUrl = pickPinImage(pin.images);\n    if (!imageUrl) {\n      throw new CommandExecutionError(`Pin ${id} has no downloadable image (it may be a video or story pin)`);\n    }\n\n    fs.mkdirSync(output, { recursive: true });\n    const ext = path.extname(new URL(imageUrl).pathname) || '.jpg';\n    const destPath = path.join(output, `${id}${ext}`);\n\n    let result;\n    try {\n      result = await httpDownload(imageUrl, destPath, { timeout: 60000 });\n    } catch (err) {\n      throw new CommandExecutionError(`Failed to download pin ${id}: ${getErrorMessage(err)}`);\n    }\n    if (!result.success) {\n      throw new CommandExecutionError(`Failed to download pin ${id}: ${result.error || 'unknown error'}`);\n    }\n\n    return [{\n      pinId: id,","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/pinterest/download.js#L22-L58","documentation":"The pin resolved but pickPinImage(pin.images) returned no usable image URL, so the command raises CommandExecutionError. Some Pinterest pins are videos or story (idea) pins whose images map contains no static downloadable rendition.","triggerScenarios":"Calling `pinterest download <id>` on a video pin, story/idea pin, or a pin whose detailed field_set images payload lacks the expected renditions.","commonSituations":"Batch-download scripts iterating a user's pins and hitting video-heavy boards; newer Pinterest formats (idea pins) that older scrapers assume are image pins.","solutions":["Only call download on image pins; detect video/story pins beforehand and skip them.","If you need video content, use a pin video resource/URL instead of the image path.","Catch CommandExecutionError and record the pin as skipped in your pipeline.","Check pin.images in the API response — if it lacks renditions, the pin is not image-based."],"exampleFix":"// before\nawait cli.download(videoPinId);\n// after\nif (pin.type === 'video' || pin.is_story) skip(pin); else await cli.download(pin.id);","handlingStrategy":"type-guard","validationCode":"const pin = await fetchPin(id);\nif (pin.type === 'video' || pin.is_story || !pin.images || Object.keys(pin.images).length === 0) {\n  skip(id, 'non-image pin');\n}","typeGuard":"function isDownloadableImagePin(pin) {\n  return Boolean(pin && pin.id && pin.images && Object.keys(pin.images).length > 0);\n}","tryCatchPattern":"try {\n  await cli.download(pinId);\n} catch (e) {\n  if (/no downloadable image/.test(e.message)) {\n    skipped.push({ id: pinId, reason: 'video-or-story-pin' });\n  } else throw e;\n}","preventionTips":["Check pin type before downloading (video/story pins have no image)","Inspect pin.images in the API payload for available renditions","Handle idea pins as a separate content type","Maintain a skip list rather than failing whole batch jobs"],"tags":["pinterest","video-pin","unsupported-content","cli"],"backgroundTag":"unsupported-media-type","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}