{"record":{"id":"82d8bdc3745cd9cc","repo":"jackwener/OpenCLI","slug":"label-must-be-a-coupang-product-url-containing","errorCode":null,"errorMessage":"${label} must be a Coupang product URL containing /vp/products/<id>","messagePattern":"(.+?) must be a Coupang product URL containing /vp/products/<id>","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/coupang/utils.js","lineNumber":113,"sourceCode":"        return '';\n    const match = text.match(/\\/vp\\/products\\/(\\d+)/) || text.match(/\\b(\\d{6,})\\b/);\n    return match?.[1] ?? '';\n}\nexport function requireProductIdArg(raw, label = '--product-id') {\n    const text = asString(raw);\n    if (label === '--url') {\n        try {\n            const url = new URL(text.startsWith('http') ? text : `https://www.coupang.com${text}`);\n            const match = url.pathname.match(/^\\/vp\\/products\\/(\\d{6,})(?:\\/|$)/);\n            const isCoupangHost = url.hostname === 'coupang.com' || url.hostname.endsWith('.coupang.com');\n            if (isCoupangHost && match) {\n                return match[1];\n            }\n        }\n        catch {\n            // Fall through to the typed validation error below.\n        }\n        throw new ArgumentError(`${label} must be a Coupang product URL containing /vp/products/<id>`);\n    }\n    if (!/^\\d{6,}$/.test(text)) {\n        throw new ArgumentError(`${label} must be a numeric Coupang product ID`);\n    }\n    return text;\n}\nexport function canonicalizeProductUrl(rawUrl, productId) {\n    const raw = asString(rawUrl);\n    if (raw) {\n        try {\n            const url = new URL(raw.startsWith('http') ? raw : `https://www.coupang.com${raw}`);\n            if (!url.hostname.includes('coupang.com'))\n                return '';\n            const id = normalizeProductId(url.pathname) || normalizeProductId(productId);\n            if (!id)\n                return url.toString();\n            return `https://www.coupang.com/vp/products/${id}`;\n        }","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/coupang/utils.js#L95-L131","documentation":"requireProductIdArg validates the --url argument in the Coupang adapter. When the label is '--url', it tries to parse the value as a URL and extract an id from a /vp/products/<id> path on a coupang.com host; if parsing fails, the host is not Coupang, or the path does not match, it throws this ArgumentError. The library fails fast with a typed error instead of proceeding with an unusable product URL.","triggerScenarios":"Calling a Coupang command with --url set to: a non-URL string, a malformed URL (e.g. 'htp://...'), a URL on a non-Coupang domain, a Coupang URL whose path is not /vp/products/<6+ digits> (e.g. a search page /search?q=... or a category page), or an id shorter than 6 digits in the path.","commonSituations":"Pasting a mobile Coupang link (m.coupang.com) with a different path shape, copying a shortened or affiliate redirect URL, passing a Coupang search/results page instead of a product detail page, or forgetting the https:// prefix in a way that breaks URL parsing.","solutions":["Open the product detail page and copy the canonical URL of the form https://www.coupang.com/vp/products/<id>.","If you already know the numeric ID, pass it with --product-id instead of --url; the numeric branch only requires 6+ digits.","Verify the URL host is coupang.com or *.coupang.com and the path starts with /vp/products/ followed by at least 6 digits.","Use normalizeProductId() as a pre-check; it also accepts bare 6+ digit ids embedded in text."],"exampleFix":"// before\nawait cli.coupang.product({ url: 'https://www.coupang.com/np/12345678' });\n// after\nawait cli.coupang.product({ url: 'https://www.coupang.com/vp/products/12345678' });\n// or: await cli.coupang.product({ productId: '12345678' });","handlingStrategy":"validation","validationCode":"function isValidCoupangProductUrl(u) {\n  try {\n    const url = new URL(u.startsWith('http') ? u : `https://www.coupang.com${u}`);\n    const isCoupang = url.hostname === 'coupang.com' || url.hostname.endsWith('.coupang.com');\n    return isCoupang && /^\\/vp\\/products\\/\\d{6,}(?:\\/|$)/.test(url.pathname);\n  } catch { return false; }\n}\nif (!isValidCoupangProductUrl(myUrl)) throw new Error('need a coupang.com /vp/products/<id> URL');","typeGuard":"function isCoupangProductUrl(v) {\n  if (typeof v !== 'string') return false;\n  try {\n    const url = new URL(v);\n    return (url.hostname === 'coupang.com' || url.hostname.endsWith('.coupang.com'))\n      && /^\\/vp\\/products\\/\\d{6,}/.test(url.pathname);\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  await cli.coupang.product({ url });\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.includes('/vp/products/')) {\n    console.error('Pass a canonical URL like https://www.coupang.com/vp/products/<id>, or use --product-id.');\n  } else throw e;\n}","preventionTips":["Always copy the canonical desktop product URL (www.coupang.com/vp/products/<id>).","Prefer passing the numeric --product-id when you already know the ID.","Keep a small isValidCoupangProductUrl helper in your scripts and call it before invoking.","Beware mobile/affiliate/shortened links — normalize them first."],"tags":["argument-validation","url-parsing","coupang"],"backgroundTag":"invalid-url-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}