{"record":{"id":"367ddd349a007635","repo":"jackwener/OpenCLI","slug":"url-must-be-on-h5-xet-citv-cn-or-a-shop-subdomain","errorCode":null,"errorMessage":"url must be on h5.xet.citv.cn or a shop subdomain (got ${parsed.hostname})","messagePattern":"url must be on h5\\.xet\\.citv\\.cn or a shop subdomain \\(got (.+?)\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xiaoe/content.js","lineNumber":102,"sourceCode":"    }\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()}\n  ${countXiaoeImages.toString()}\n  const selectors = ${JSON.stringify(CONTENT_SELECTORS)};\n  const title = document.title || '';\n  const content = pickContentText(document, selectors, ${JSON.stringify(CONTENT_MIN_LENGTH)});\n  const imageCount = countXiaoeImages(document);\n  return [{\n    title,","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoe/content.js#L84-L120","documentation":"This ArgumentError is thrown by requireXiaoePageUrl when a xiaoe URL is passed whose hostname is not h5.xet.citv.cn and not a shop subdomain of it (e.g. appxxxx.h5.xet.citv.cn). The library only knows how to render and extract content from pages hosted on the xiaoe (小鹅通) H5 domain, so it rejects any other host up front before launching a browser. It is a pre-flight input validation guard.","triggerScenarios":"Calling `opencli xiaoe <command> <url>` with a URL pointing at a different host, e.g. xiaoe-tech.com, study.xiaoe-tech.com, a typo'd domain (h5.xit.citv.cn), http-vs-https confusion resolved to another host, or a re-hosted/proxied share link.","commonSituations":"Pasting a course link copied from WeChat that redirects through a different domain; using the management-console domain (study.xiaoe-tech.com) instead of the reader H5 domain; a shop migrating to a custom domain.","solutions":["Use a URL on h5.xet.citv.cn or a subdomain, e.g. https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx","Check parsed.hostname for typos (extra dashes, wrong TLD) and correct it","If the shop uses a custom domain, ask the library maintainers or host the page under the *.h5.xet.citv.cn domain instead","For course listing, use the xiaoe/courses command which targets study.xiaoe-tech.com instead"],"exampleFix":"// before\nopencli xiaoe content https://app1234.xiaoe-tech.com/p/course/ecourse/v_abc\n// after\nopencli xiaoe content https://app1234.h5.xet.citv.cn/p/course/ecourse/v_abc","handlingStrategy":"validation","validationCode":"function isXiaoePageUrl(url) {\n  try {\n    const h = new URL(url).hostname.toLowerCase();\n    return h === 'h5.xet.citv.cn' || h.endsWith('.h5.xet.citv.cn');\n  } catch { return false; }\n}\nif (!isXiaoePageUrl(input)) throw new Error('not a xiaoe h5 URL: ' + input);","typeGuard":"function isXiaoePageUrl(v) {\n  try {\n    const { hostname } = new URL(String(v));\n    return hostname.toLowerCase() === 'h5.xet.citv.cn' || hostname.toLowerCase().endsWith('.h5.xet.citv.cn');\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  await runXiaoeCommand(url);\n} catch (e) {\n  if (e.name === 'ArgumentError' && /must be on h5\\.xet\\.citv\\.cn/.test(e.message)) {\n    console.error('Use a URL like https://appxxxx.h5.xet.citv.cn/... — got: ' + url);\n  } else throw e;\n}","preventionTips":["Only paste links whose host ends in .h5.xet.citv.cn into xiaoe commands","Normalize URLs by parsing with new URL() and checking hostname before calling","Watch for WeChat-shared links that redirect to alternate domains","Use https explicitly; the same validator also requires it"],"tags":["url-validation","argument-error","xiaoe"],"backgroundTag":"invalid-host-url","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}