{"record":{"id":"6631629cd810c476","repo":"jackwener/OpenCLI","slug":"url-is-required-positional","errorCode":null,"errorMessage":"url is required (positional)","messagePattern":"url is required \\(positional\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xiaoe/content.js","lineNumber":83,"sourceCode":"// Pure: count `<img>` elements whose src looks like a real xiaoe-hosted\n// asset. `data:` URIs and non-xiaoe CDNs (avatars, ads) are excluded.\nexport function countXiaoeImages(doc) {\n    let count = 0;\n    const imgs = doc.querySelectorAll('img');\n    for (let i = 0; i < imgs.length; i += 1) {\n        const src = imgs[i].getAttribute('src') || imgs[i].src || '';\n        if (!src) continue;\n        if (src.startsWith('data:')) continue;\n        if (!src.includes('xiaoe')) continue;\n        count += 1;\n    }\n    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')) {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoe/content.js#L65-L101","documentation":"requireXiaoePageUrl validates the positional url argument for xiaoe content commands. If the value is missing, empty, or not a non-empty string after trim, it throws ArgumentError('url is required (positional)').","triggerScenarios":"Invoking a xiaoe content command (e.g. opencli xiaoe <subcommand>) without the positional URL, with an empty string, or passing the option in the wrong place so the parser yields undefined.","commonSituations":"Forgot the argument entirely; pasted only flags/options; shell quoting swallowed the URL; CLI argument-order change between versions leaving url undefined.","solutions":["Pass the xiaoe page URL as the positional argument: opencli xiaoe <command> https://appxxxx.h5.xet.citv.cn/p/course/...","Quote the URL in the shell so special characters are not stripped.","Run the command with --help to confirm the expected argument order.","Check your wrapper script isn't dropping an empty/undefined variable into the URL slot."],"exampleFix":"// before\nopencli xiaoe content --format json\n// after\nopencli xiaoe content 'https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx' --format json","handlingStrategy":"validation","validationCode":"const url = process.argv[2];\nif (!url || !url.trim()) {\n  console.error('usage: opencli xiaoe <command> <url>');\n  process.exit(2);\n}","typeGuard":"const hasUrl = v => typeof v === 'string' && v.trim().length > 0;","tryCatchPattern":"try {\n  await runCommand(args);\n} catch (e) {\n  if (e instanceof ArgumentError && /url is required/.test(e.message)) {\n    console.error('Pass the xiaoe page URL as the first positional argument.');\n    process.exit(2);\n  } else throw e;\n}","preventionTips":["Quote positional URLs in shell scripts.","Check --help output for argument order after upgrading the CLI.","Validate inputs in wrapper scripts before invoking the CLI."],"tags":["argument-validation","cli","xiaoe"],"backgroundTag":"missing-required-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}