{"record":{"id":"311e3bb40e3f2010","repo":"jackwener/OpenCLI","slug":"url-must-follow-username-video-id-format","errorCode":null,"errorMessage":"URL must follow /@<username>/video/<id> format","messagePattern":"URL must follow /@<username>/video/<id> format","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/tiktok/utils.js","lineNumber":129,"sourceCode":"    }\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 {\n        url: parsed.toString(),\n        username: match[1],\n        videoId: match[2],\n    };\n}\n\nexport function looksTikTokAuthFailure(message) {\n    return /\\bAUTH_REQUIRED\\b|\\b(auth|captcha|login|log in|permission|unauthori[sz]ed|forbidden)\\b|HTTP\\s+(401|403)\\b/i.test(String(message || ''));\n}\n\nexport function looksTikTokUpstreamFailure(message) {\n    return /\\b(API failed|HTTP\\s+\\d+|invalid JSON|Failed to fetch|network|fetch)\\b/i.test(String(message || ''));\n}","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/tiktok/utils.js#L111-L147","documentation":"The URL must be the canonical /@<username>/video/<numeric-id> path. Share links (vm.tiktok.com/...), bare video IDs, and other tiktok.com paths are explicitly out of scope, so the pathname regex /\\/^\\/@([A-Za-z0-9._-]+)\\/video\\/(\\d+)\\/?$/ failing produces this ArgumentError.","triggerScenarios":"Passing a valid https tiktok.com URL whose pathname is not /@user/video/<digits> — e.g. vm.tiktok.com/abc123 share links, /t/ links, photo posts (/photo/<id>), or a video ID with non-digit characters.","commonSituations":"Copying a share link from the mobile app; URL-encoding or trailing junk in the path; username containing characters outside [A-Za-z0-9._-]; passing a video ID instead of a full URL.","solutions":["Open the share link in a browser and copy the resolved canonical /@user/video/<id> URL","Verify the path matches /@<username>/video/<numeric id> (note the @ prefix)","Strip query strings is not required (they are allowed), but ensure the pathname itself is the canonical video path"],"exampleFix":"// before\nparseTikTokVideoUrl('https://vm.tiktok.com/ZMabc123/')\n// after\nparseTikTokVideoUrl('https://www.tiktok.com/@user/video/1234567890')","handlingStrategy":"validation","validationCode":"const u = new URL(value); if (!/^\\/@[A-Za-z0-9._-]+\\/video\\/\\d+\\/?$/.test(u.pathname)) throw new Error('not a canonical /@user/video/<id> URL');","typeGuard":"const isCanonicalTikTokVideoUrl = (v) => { try { return /^\\/@[A-Za-z0-9._-]+\\/video\\/\\d+\\/?$/.test(new URL(String(v)).pathname); } catch { return false; } };","tryCatchPattern":"try { parseTikTokVideoUrl(url); } catch (e) { if (/format/.test(e.message)) console.error('Resolve share links to /@user/video/<id> first'); else throw e; }","preventionTips":["Never feed vm.tiktok.com share links directly; resolve them in a browser first","Regex-test the pathname in your pipeline before calling the CLI","Convert bare video IDs to full canonical URLs upstream"],"tags":["validation","url-format","argument-error"],"backgroundTag":"invalid-url-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}