jackwener/OpenCLI · error · ArgumentError
nowcoder detail only accepts canonical https://www.nowcoder.
Error message
nowcoder detail only accepts canonical https://www.nowcoder.com post URLs
What it means
If the input parses as a URL but is not a canonical Nowcoder post link, parseNowcoderPostTarget throws this ArgumentError. The URL must use https, have no credentials, port, or hash, and its host must be nowcoder.com or www.nowcoder.com. This enforces that only unambiguous, official post URLs are accepted so the target can be mapped deterministically to a post id.
Source
Thrown at clis/nowcoder/posts.js:192
return rows.slice(0, limit);
}
export function parseNowcoderPostTarget(raw) {
const value = typeof raw === 'string' ? raw.trim() : '';
if (NUMERIC_ID_PATTERN.test(value)) return { post_type: 'content', value };
if (UUID_PATTERN.test(value)) return { post_type: 'moment', value: value.toLowerCase() };
let url;
try {
url = new URL(value);
}
catch {
throw new ArgumentError('nowcoder detail requires a numeric content ID, moment UUID, or canonical Nowcoder URL');
}
const host = url.hostname.toLowerCase();
if (url.protocol !== 'https:' || url.username || url.password || url.port || url.hash
|| (host !== 'nowcoder.com' && host !== 'www.nowcoder.com')) {
throw new ArgumentError('nowcoder detail only accepts canonical https://www.nowcoder.com post URLs');
}
const content = url.pathname.match(/^\/discuss\/([1-9]\d*)\/?$/);
if (content) return { post_type: 'content', value: content[1] };
const moment = url.pathname.match(/^\/feed\/main\/detail\/([0-9a-f]{32})\/?$/i);
if (moment) return { post_type: 'moment', value: moment[1].toLowerCase() };
throw new ArgumentError('Unsupported Nowcoder URL; expected /discuss/<content-id> or /feed/main/detail/<moment-uuid>');
}
export function projectNowcoderDetail(data, target) {
if (!isRecord(data) || !isRecord(data.frequencyData)) throw new CommandExecutionError('Nowcoder detail returned malformed post data');
const isContent = target.post_type === 'content';
const expectedEntityType = isContent ? CONTENT_ENTITY_TYPE : MOMENT_TYPE;
if (data.entityType !== expectedEntityType) throw new CommandExecutionError('Nowcoder detail returned a mismatched post entity type');
const uuid = requiredUuid(data.uuid, `${target.post_type} uuid`);
const entityId = requiredId(data.entityId, `${target.post_type} entity id`);
const id = isContent ? requiredId(data.id, 'content id') : uuid;
if (isContent ? id !== target.value : uuid !== target.value) throw new CommandExecutionError('Nowcoder detail returned a different post identity');
if (!isContent && entityId !== requiredId(data.id, 'moment id')) throw new CommandExecutionError('Nowcoder detail returned mismatched moment entity ids');View on GitHub (pinned to 49907e53dc)
Solutions
- Rewrite the URL to start with https://www.nowcoder.com/.
- Strip any #fragment, port, or credentials from the URL.
- For mobile links (m.nowcoder.com), reconstruct the canonical /discuss/<id> path manually.
- Alternatively bypass URL parsing by passing the bare numeric content ID or moment UUID directly.
Example fix
// before nowcoder detail http://m.nowcoder.com/discuss/1234567#comments // after nowcoder detail https://www.nowcoder.com/discuss/1234567
Defensive patterns
Strategy: validation
Validate before calling
function isCanonicalNowcoderUrl(raw) {
let url;
try { url = new URL(typeof raw === 'string' ? raw.trim() : ''); } catch { return false; }
const host = url.hostname.toLowerCase();
return url.protocol === 'https:' && !url.username && !url.password && !url.port && !url.hash
&& (host === 'nowcoder.com' || host === 'www.nowcoder.com')
&& (/^\/discuss\/[1-9]\d*\/?$/.test(url.pathname) || /^\/feed\/main\/detail\/[0-9a-f]{32}\/?$/i.test(url.pathname));
} Type guard
function isCanonicalNowcoderPostUrl(value) { try { const u = new URL(value); return u.protocol === 'https:' && (u.hostname === 'www.nowcoder.com' || u.hostname === 'nowcoder.com') && /^\/discuss\/[1-9]\d*\/?$|^\/feed\/main\/detail\/[0-9a-f]{32}\/?$/i.test(u.pathname); } catch { return false; } } Try / catch
try {
target = parseNowcoderPostTarget(raw);
} catch (error) {
if (error instanceof ArgumentError && String(error.message).includes('canonical')) {
console.error('Rewrite the link as https://www.nowcoder.com/discuss/<id> or /feed/main/detail/<uuid>, with no port, credentials, or #fragment');
}
throw error;
} Prevention
- Normalize http:// and mobile-host links to https://www.nowcoder.com before use
- Strip #fragments, ports, and credentials from copied URLs
- Prefer the bare post id/UUID when the canonical URL is uncertain
- Run isCanonicalNowcoderPostUrl as a pre-check in scripts
When it happens
Trigger: A URL-parseable input that fails the canonicality check: http:// instead of https://, embedded username/password or a port, a #fragment, or a host other than nowcoder.com/www.nowcoder.com (e.g., m.nowcoder.com, a mirror, or a different site entirely).
Common situations: Users copying http:// links from old bookmarks; mobile-host links (m.nowcoder.com); URLs with tracking fragments (#comment-1) or query-ordered ports; pasting links from other sites into the nowcoder detail command.
Related errors
- nowcoder detail requires a numeric content ID, moment UUID,
- 1688 item expects an offer URL or offer ID
- Invalid 1688 store URL
- Invalid 1688 URL
- archive snapshots url cannot be empty
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/47517150699b7e31.
Report an issue: GitHub.