{"record":{"id":"70cb159fa23c4e7b","repo":"jackwener/OpenCLI","slug":"invalid-video-url-raw","errorCode":null,"errorMessage":"invalid video URL: ${raw}","messagePattern":"invalid video URL: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/tiktok/utils.js","lineNumber":116,"sourceCode":"\n// Parses a TikTok video URL into {username, videoId, url}. Rejects bad\n// shapes up-front so we never `goto()` an arbitrary page and silently\n// pretend the click target was found. Both share-style links\n// (vm.tiktok.com/...) and bare video IDs are out of scope — callers must\n// pass the canonical /@user/video/id form.\nexport function parseTikTokVideoUrl(value) {\n    const raw = String(value ?? '').trim();\n    if (!raw) {\n        throw new ArgumentError(\n            'video URL is required',\n            'Example: opencli tiktok comment https://www.tiktok.com/@user/video/1234567890 \"...\"',\n        );\n    }\n    let parsed;\n    try {\n        parsed = new URL(raw);\n    } catch {\n        throw new ArgumentError(\n            `invalid video URL: ${raw}`,\n            'Example: https://www.tiktok.com/@user/video/1234567890',\n        );\n    }\n    if (!/(^|\\.)tiktok\\.com$/i.test(parsed.hostname)) {\n        throw new ArgumentError(\n            `URL must be on tiktok.com (got ${parsed.hostname})`,\n            'Example: https://www.tiktok.com/@user/video/1234567890',\n        );\n    }\n    const match = parsed.pathname.match(/^\\/@([A-Za-z0-9._-]+)\\/video\\/(\\d+)\\/?$/);\n    if (!match) {\n        throw new ArgumentError(\n            'URL must follow /@<username>/video/<id> format',\n            'Example: https://www.tiktok.com/@user/video/1234567890',\n        );\n    }\n    return {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/tiktok/utils.js#L98-L134","documentation":"parseTikTokVideoUrl runs new URL(raw) to parse the input; if the string is not a valid absolute URL, the constructor throws and this ArgumentError is raised with the raw input echoed back.","triggerScenarios":"Passing a bare video ID, a share link without scheme (www.tiktok.com/...), or any string that fails the URL constructor.","commonSituations":"Passing '1234567890' instead of a URL; copy-pasting a URL that lost its https:// scheme; vm.tiktok.com share links are also fine as URLs but fail later at the path check — this one fires before host checking.","solutions":["Prefix the value with the scheme: https:// before the hostname","Pass the full canonical form https://www.tiktok.com/@user/video/<id>","If you only have a video ID, construct the URL yourself: `https://www.tiktok.com/@user/video/${id}`"],"exampleFix":"// before\nparseTikTokVideoUrl('www.tiktok.com/@user/video/123')\n// after\nparseTikTokVideoUrl('https://www.tiktok.com/@user/video/123')","handlingStrategy":"validation","validationCode":"new URL(value); // will throw SyntaxError early if not an absolute URL","typeGuard":"const isAbsoluteUrl = (v) => { try { return Boolean(new URL(String(v))); } catch { return false; } };","tryCatchPattern":"try { parseTikTokVideoUrl(raw); } catch (e) { if (/^invalid video URL:/.test(e.message)) console.error('Include the https:// scheme'); else throw e; }","preventionTips":["Always include https:// when storing TikTok URLs","Normalize IDs to full URLs at ingestion time","Lint configs for URL-typed fields with new URL()"],"tags":["validation","url-parsing","argument-error"],"backgroundTag":"invalid-url-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}