{"record":{"id":"c75143892ab29c52","repo":"jackwener/OpenCLI","slug":"nowcoder-detail-requires-a-numeric-content-id-mom","errorCode":null,"errorMessage":"nowcoder detail requires a numeric content ID, moment UUID, or canonical Nowcoder URL","messagePattern":"nowcoder detail requires a numeric content ID, moment UUID, or canonical Nowcoder URL","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/nowcoder/posts.js","lineNumber":187,"sourceCode":"    for (const record of records) {\n        const data = wrapped ? record?.data : record;\n        rows.push(projectFeedData(data, rows.length, source === 'experience' || wrapped));\n    }\n    if (rows.length === 0) throw new EmptyResultError(`nowcoder ${source}`, 'Nowcoder returned no content or moment posts.');\n    return rows.slice(0, limit);\n}\n\nexport function parseNowcoderPostTarget(raw) {\n    const value = typeof raw === 'string' ? raw.trim() : '';\n    if (NUMERIC_ID_PATTERN.test(value)) return { post_type: 'content', value };\n    if (UUID_PATTERN.test(value)) return { post_type: 'moment', value: value.toLowerCase() };\n\n    let url;\n    try {\n        url = new URL(value);\n    }\n    catch {\n        throw new ArgumentError('nowcoder detail requires a numeric content ID, moment UUID, or canonical Nowcoder URL');\n    }\n    const host = url.hostname.toLowerCase();\n    if (url.protocol !== 'https:' || url.username || url.password || url.port || url.hash\n        || (host !== 'nowcoder.com' && host !== 'www.nowcoder.com')) {\n        throw new ArgumentError('nowcoder detail only accepts canonical https://www.nowcoder.com post URLs');\n    }\n    const content = url.pathname.match(/^\\/discuss\\/([1-9]\\d*)\\/?$/);\n    if (content) return { post_type: 'content', value: content[1] };\n    const moment = url.pathname.match(/^\\/feed\\/main\\/detail\\/([0-9a-f]{32})\\/?$/i);\n    if (moment) return { post_type: 'moment', value: moment[1].toLowerCase() };\n    throw new ArgumentError('Unsupported Nowcoder URL; expected /discuss/<content-id> or /feed/main/detail/<moment-uuid>');\n}\n\nexport function projectNowcoderDetail(data, target) {\n    if (!isRecord(data) || !isRecord(data.frequencyData)) throw new CommandExecutionError('Nowcoder detail returned malformed post data');\n    const isContent = target.post_type === 'content';\n    const expectedEntityType = isContent ? CONTENT_ENTITY_TYPE : MOMENT_TYPE;\n    if (data.entityType !== expectedEntityType) throw new CommandExecutionError('Nowcoder detail returned a mismatched post entity type');","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/nowcoder/posts.js#L169-L205","documentation":"parseNowcoderPostTarget accepts a numeric content ID, a 32-hex moment UUID, or a canonical Nowcoder URL. When the raw input is none of those and also cannot be parsed by the URL constructor, it throws this ArgumentError telling the user exactly which three input forms are accepted. It is a user-input validation error, not a network or data error.","triggerScenarios":"Calling parseNowcoderPostTarget (via the 'nowcoder detail' command target) with a string that is not numeric, not a 32-char hex UUID, and not URL-parseable — e.g., 'abc', 'nowcoder.com/discuss/123' without scheme (actually parses as relative and fails new URL), an empty-ish string with only whitespace, or a non-string coerced to ''.","commonSituations":"Users pasting a bare domain-less URL like 'www.nowcoder.com/discuss/123456' without https://; shell quoting stripping characters from a URL; typos in a hand-typed post id; passing an int instead of a string so it fails both patterns.","solutions":["Pass a plain numeric content ID (e.g., '1234567') for discuss posts.","Pass a 32-character hex moment UUID, or a full URL starting with https://www.nowcoder.com/.","If pasting a URL, ensure it includes the https:// scheme — bare 'www.nowcoder.com/...' will not parse.","Quote the argument in your shell so special characters (?, &) are not stripped."],"exampleFix":"// before\nnowcoder detail www.nowcoder.com/discuss/1234567\n// after\nnowcoder detail https://www.nowcoder.com/discuss/1234567","handlingStrategy":"validation","validationCode":"const NUMERIC = /^[1-9]\\d*$/;\nconst UUID = /^[0-9a-f]{32}$/i;\nfunction isValidTarget(raw) {\n    const v = typeof raw === 'string' ? raw.trim() : '';\n    if (NUMERIC.test(v) || UUID.test(v)) return true;\n    try { new URL(v); return v.startsWith('https://www.nowcoder.com/') || v.startsWith('https://nowcoder.com/'); }\n    catch { return false; }\n}","typeGuard":"function isNowcoderTarget(raw) { const v = typeof raw === 'string' ? raw.trim() : ''; return /^[1-9]\\d*$/.test(v) || /^[0-9a-f]{32}$/i.test(v); }","tryCatchPattern":"try {\n    target = parseNowcoderPostTarget(raw);\n} catch (error) {\n    if (error instanceof ArgumentError) {\n        console.error('Pass a numeric content ID, 32-hex moment UUID, or full https://www.nowcoder.com URL');\n        process.exitCode = 2;\n    } else throw error;\n}","preventionTips":["Always include the https:// scheme when passing a URL","Prefer passing the bare numeric ID or UUID to avoid URL pitfalls","Quote URLs in the shell so ?, &, and # survive","Trim whitespace from user input before invoking the command"],"tags":["argument-validation","user-input","url-parsing"],"backgroundTag":"invalid-argument-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}