{"record":{"id":"eb504416d201e99c","repo":"jackwener/OpenCLI","slug":"xiaoe-url-must-use-https-got-parsed-protocol-re","errorCode":null,"errorMessage":"xiaoe URL must use https (got ${parsed.protocol.replace(':', '')})","messagePattern":"xiaoe URL must use https \\(got (.+?)\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xiaoe/content.js","lineNumber":95,"sourceCode":"    return count;\n}\n\nexport function requireXiaoePageUrl(value, commandName) {\n    const raw = typeof value === 'string' ? value.trim() : '';\n    if (!raw) {\n        throw new ArgumentError('url is required (positional)');\n    }\n    let parsed;\n    try {\n        parsed = new URL(raw);\n    } catch {\n        throw new ArgumentError(\n            `invalid xiaoe URL: ${raw}`,\n            `Example: opencli xiaoe ${commandName} https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx`,\n        );\n    }\n    if (parsed.protocol !== 'https:') {\n        throw new ArgumentError(\n            `xiaoe URL must use https (got ${parsed.protocol.replace(':', '')})`,\n            `Example: opencli xiaoe ${commandName} https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx`,\n        );\n    }\n    const host = parsed.hostname.toLowerCase();\n    if (host !== 'h5.xet.citv.cn' && !host.endsWith('.h5.xet.citv.cn')) {\n        throw new ArgumentError(\n            `url must be on h5.xet.citv.cn or a shop subdomain (got ${parsed.hostname})`,\n            `Example: opencli xiaoe ${commandName} https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx`,\n        );\n    }\n    return parsed.toString();\n}\n\nexport function buildContentScript() {\n    return `\n(() => {\n  ${pickContentText.toString()}","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoe/content.js#L77-L113","documentation":"After parsing, requireXiaoePageUrl enforces the https protocol. If parsed.protocol !== 'https:' it throws ArgumentError telling you which scheme was received, with the expected URL format as the hint.","triggerScenarios":"Supplying an http://, file://, or other non-https URL as the positional url to a xiaoe content command.","commonSituations":"Copy-pasting an http:// link from old documentation or an internal mirror; using a file:// path for a saved page; Xiaoe-hosted pages always requiring TLS so http links would fail anyway.","solutions":["Replace the scheme with https:// before invoking the command.","If a site redirects http→https, use the final https URL directly.","Never pass local file:// paths; load saved pages through a local https server if needed.","Pre-validate the scheme with new URL(raw).protocol === 'https:' in calling code."],"exampleFix":"// before\nopencli xiaoe content 'http://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx'\n// after\nopencli xiaoe content 'https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx'","handlingStrategy":"validation","validationCode":"const u = new URL(raw);\nif (u.protocol !== 'https:') throw new Error(`use https, got ${u.protocol}`);","typeGuard":"const isHttps = v => { try { return new URL(v).protocol === 'https:'; } catch { return false; } };","tryCatchPattern":"try {\n  await contentCommand(url);\n} catch (e) {\n  if (e instanceof ArgumentError && /must use https/.test(e.message)) {\n    url = url.replace(/^http:\\/\\//, 'https://');\n    await contentCommand(url);\n  } else throw e;\n}","preventionTips":["Normalize http:// links to https:// before invoking xiaoe commands.","Store xiaoe URLs with the https scheme in config files.","Reject file:// or other schemes at input boundaries."],"tags":["argument-validation","url-parsing","https","xiaoe"],"backgroundTag":"invalid-url-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}