{"record":{"id":"d0765cf00b6482a3","repo":"jackwener/OpenCLI","slug":"invalid-tweet-url-host-value","errorCode":null,"errorMessage":"Invalid tweet URL host: ${value}","messagePattern":"Invalid tweet URL host: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/twitter/shared.js","lineNumber":61,"sourceCode":"        || hostname.endsWith('.x.com')\n        || hostname.endsWith('.twitter.com');\n}\n\nexport function parseTweetUrl(rawUrl) {\n    const value = String(rawUrl ?? '').trim();\n    if (!value) {\n        throw new ArgumentError('twitter tweet URL cannot be empty', 'Example: opencli twitter retweet https://x.com/jack/status/20');\n    }\n    let parsed;\n    try {\n        parsed = new URL(value);\n    }\n    catch {\n        throw new ArgumentError(`Invalid tweet URL: ${value}`, 'Use a full https://x.com/<user>/status/<id> URL');\n    }\n    const hostname = parsed.hostname.toLowerCase();\n    if (parsed.protocol !== 'https:' || !isTwitterHost(hostname)) {\n        throw new ArgumentError(`Invalid tweet URL host: ${value}`, 'Use a full https://x.com/<user>/status/<id> URL');\n    }\n    const match = parsed.pathname.match(TWEET_PATH_PATTERN);\n    if (!match?.[1]) {\n        throw new ArgumentError(`Could not extract tweet ID from URL: ${value}`, 'Use a full https://x.com/<user>/status/<id> URL');\n    }\n    return {\n        id: match[1],\n        url: parsed.toString(),\n    };\n}\n\n/**\n * Build a JS source fragment that, when embedded inside a `page.evaluate(...)`\n * IIFE, declares browser-side helpers for scoping operations to a specific\n * tweet by status id. Sibling adapters historically inlined ad-hoc article\n * lookups that either (a) skipped scoping entirely (silent: act on first\n * matching button on a conversation page) or (b) used substring matches like\n * `pathname.includes('/status/' + tweetId)` (silent: `/status/123` matches","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/shared.js#L43-L79","documentation":"ArgumentError from parseTweetUrl when the URL parses but its protocol is not https: or its hostname is not a twitter/x.com host (isTwitterHost). Only https://(www.)(x.com|twitter.com) URLs are accepted, so other schemes/hosts are rejected before extracting the tweet ID.","triggerScenarios":"Passing http:// (not https), mobile.twitter.com variants outside the accepted suffix set, a t.co short link, a facebook/reddit URL, or javascript:/file: URIs.","commonSituations":"Copying an http:// link from old logs, pasting a shortened t.co URL from a tweet, pointing at a mirror/ninga-style frontend host, or test fixtures with example.com URLs.","solutions":["Use an https://x.com/<user>/status/<id> URL (or https://twitter.com/... equivalent)","Expand t.co short links to the real x.com status URL first","Change http:// to https:// if the host is otherwise correct"],"exampleFix":"// before\nopencli twitter retweet http://x.com/jack/status/20\n// after\nopencli twitter retweet https://x.com/jack/status/20","handlingStrategy":"validation","validationCode":"function isTweetHostUrl(v) {\n  try {\n    const u = new URL(String(v).trim());\n    const okHost = u.hostname === 'x.com' || u.hostname === 'twitter.com' || u.hostname.endsWith('.twitter.com');\n    return u.protocol === 'https:' && okHost && /\\/status\\/\\d+/.test(u.pathname);\n  } catch { return false; }\n}\nif (!isTweetHostUrl(url)) throw new Error('expected https://x.com/<user>/status/<id>');","typeGuard":"function isHttpsTwitterUrl(v) {\n  try { const u = new URL(String(v).trim()); return u.protocol === 'https:' && /(^|\\.)(x\\.com|twitter\\.com)$/.test(u.hostname); } catch { return false; }\n}","tryCatchPattern":"try {\n  await opencli.twitter.retweet(url);\n} catch (e) {\n  if (e.name === 'ArgumentError' && /host/.test(e.message)) {\n    console.error('Only https://x.com or https://twitter.com status URLs are supported');\n  } else throw e;\n}","preventionTips":["Use https:// only — rewrite any http:// links","Expand t.co and other short links before passing","Only pass x.com/twitter.com status URLs, not mirrors or other sites","Normalize hosts (strip mobile./www.) with a whitelist check"],"tags":["validation","argument","twitter","url","host"],"backgroundTag":"invalid-url-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}