{"record":{"id":"90c8395710b713a5","repo":"jackwener/OpenCLI","slug":"unsupported-nowcoder-url-expected-discuss-conte","errorCode":null,"errorMessage":"Unsupported Nowcoder URL; expected /discuss/<content-id> or /feed/main/detail/<moment-uuid>","messagePattern":"Unsupported Nowcoder URL; expected /discuss/<content-id> or /feed/main/detail/<moment-uuid>","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/nowcoder/posts.js","lineNumber":198,"sourceCode":"    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');\n    const uuid = requiredUuid(data.uuid, `${target.post_type} uuid`);\n    const entityId = requiredId(data.entityId, `${target.post_type} entity id`);\n    const id = isContent ? requiredId(data.id, 'content id') : uuid;\n    if (isContent ? id !== target.value : uuid !== target.value) throw new CommandExecutionError('Nowcoder detail returned a different post identity');\n    if (!isContent && entityId !== requiredId(data.id, 'moment id')) throw new CommandExecutionError('Nowcoder detail returned mismatched moment entity ids');\n    const expectedAuthorId = isContent ? data.authorId : data.userId;\n    const body = cleanBody(isContent ? data.richText : data.content, `${target.post_type} detail body`);\n    return {\n        post_type: target.post_type,\n        id,\n        uuid,","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/nowcoder/posts.js#L180-L216","documentation":"parseNowcoderPostTarget accepts a numeric content ID, a moment UUID, or a canonical https://www.nowcoder.com URL. After passing the scheme/host checks, the URL pathname must match either /discuss/<content-id> (positive integer) or /feed/main/detail/<32-hex-char uuid>. Any other well-formed nowcoder.com URL path throws this ArgumentError, because the CLI cannot determine which post detail endpoint to call.","triggerScenarios":"Calling nowcoder detail with a nowcoder.com URL whose path is neither /discuss/<numeric-id> nor /feed/main/detail/<32-hex uuid> — e.g. a /interview/main/... link, a /discuss/ path with a non-numeric or zero-padded id (012345), a /feed/main/detail/ uuid that is not exactly 32 hex chars, or a URL with a query string altering the expectation of a trailing path segment.","commonSituations":"Pasting a Nowcoder link copied from the mobile app or a share dialog that uses a different route format; using a shortened or relative link; passing a URL for another nowcoder product page (jobs, campus, interview questions); typos in the URL; leading zeros or URL-encoded IDs in /discuss/ links.","solutions":["Use a canonical URL of the form https://www.nowcoder.com/discuss/<numeric-id> or https://www.nowcoder.com/feed/main/detail/<32-hex-uuid>.","Alternatively pass just the numeric content ID or the moment UUID string instead of a URL.","Check the URL for extra path segments, leading zeros, or non-hex characters and correct them.","If the post is not a discuss post or a feed moment, it is not supported — locate the discuss/feed version of the content."],"exampleFix":"// before\nnowcoder detail \"https://www.nowcoder.com/interview/ai/index\"\n// after\nnowcoder detail \"https://www.nowcoder.com/discuss/70123456\"","handlingStrategy":"validation","validationCode":"function isCanonicalNowcoderUrl(value) {\n  if (!/^\\d+$/.test(value)) return true; // bare IDs/UUIDs are fine\n  try {\n    const url = new URL(value);\n    const okHost = ['nowcoder.com', 'www.nowcoder.com'].includes(url.hostname.toLowerCase());\n    const okPath = /^\\/discuss\\/[1-9]\\d*\\/?$/.test(url.pathname)\n      || /^\\/feed\\/main\\/detail\\/[0-9a-f]{32}\\/?$/i.test(url.pathname);\n    return url.protocol === 'https:' && okHost && okPath;\n  } catch { return true; }\n}","typeGuard":"function isNowcoderDiscussOrFeedUrl(v) {\n  return typeof v === 'string' && (/^https:\\/\\/(www\\.)?nowcoder\\.com\\/discuss\\/[1-9]\\d*\\/?$/.test(v)\n    || /^https:\\/\\/(www\\.)?nowcoder\\.com\\/feed\\/main\\/detail\\/[0-9a-f]{32}\\/?$/i.test(v));\n}","tryCatchPattern":"try {\n  await nowcoderDetail(target);\n} catch (err) {\n  if (err instanceof ArgumentError && /Unsupported Nowcoder URL/.test(err.message)) {\n    console.error('Pass /discuss/<id>, /feed/main/detail/<uuid>, or a bare ID/UUID:', err.message);\n  } else throw err;\n}","preventionTips":["Copy URLs directly from the post's canonical page (discuss/ or feed/main/detail/ routes).","Prefer passing the bare numeric ID or 32-char moment UUID instead of a URL.","Strip query strings and short-link redirects before passing a URL.","Validate the URL shape with a regex in wrapper scripts before invoking the CLI."],"tags":["argument-validation","url-parsing","input-validation"],"backgroundTag":"unsupported-url-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}