{"record":{"id":"4420425fd9fef1f9","repo":"jackwener/OpenCLI","slug":"unexpected-xiaohongshu-search-harvest-row-index-442042","errorCode":null,"errorMessage":"Unexpected Xiaohongshu search harvest row ${index + 1} URL; expected a trusted note URL.","messagePattern":"Unexpected Xiaohongshu search harvest row (.+?) URL; expected a trusted note URL\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/search.js","lineNumber":301,"sourceCode":"        return parsed.protocol === 'https:' &&\n            parsed.hostname.toLowerCase() === webHost.toLowerCase() &&\n            /^\\/user\\/profile\\/[^/]+\\/?$/i.test(parsed.pathname);\n    }\n    catch {\n        return false;\n    }\n}\nfunction requireTrustedHarvestRow(row, index, webHost) {\n    if (!row || typeof row !== 'object' || Array.isArray(row)) {\n        throw new CommandExecutionError(`Unexpected Xiaohongshu search harvest row ${index + 1} shape; expected an object.`);\n    }\n    for (const field of ['title', 'author', 'likes', 'url', 'author_url']) {\n        if (typeof row[field] !== 'string') {\n            throw new CommandExecutionError(`Unexpected Xiaohongshu search harvest row ${index + 1} shape; expected string ${field}.`);\n        }\n    }\n    if (!noteUrlInfo(row.url, webHost).key) {\n        throw new CommandExecutionError(`Unexpected Xiaohongshu search harvest row ${index + 1} URL; expected a trusted note URL.`);\n    }\n    if (!isTrustedAuthorUrl(row.author_url, webHost)) {\n        throw new CommandExecutionError(`Unexpected Xiaohongshu search harvest row ${index + 1} author URL; expected a trusted profile URL.`);\n    }\n    return row;\n}\nfunction requireHarvestPayload(payload, webHost) {\n    const result = unwrapEvaluateResult(payload);\n    const diag = result?.diag;\n    if (!result || typeof result !== 'object' || Array.isArray(result) || !Array.isArray(result.rows) ||\n        !diag || typeof diag !== 'object' || Array.isArray(diag) ||\n        typeof diag.securityBlock !== 'boolean' || typeof diag.stopReason !== 'string' ||\n        !Number.isFinite(diag.scrollHeight) || diag.scrollHeight < 0 ||\n        !Number.isFinite(diag.clientHeight) || diag.clientHeight < 0 ||\n        !Number.isSafeInteger(diag.cardCount) || diag.cardCount < 0 ||\n        !(diag.feedClientHeight === null || (Number.isFinite(diag.feedClientHeight) && diag.feedClientHeight >= 0)) ||\n        !Number.isSafeInteger(diag.distinctCardTops) || diag.distinctCardTops < 0) {\n        throw new CommandExecutionError('Unexpected Xiaohongshu search harvest payload shape; expected rows plus typed diagnostics.');","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/search.js#L283-L319","documentation":"The row's url field must parse to a trusted note URL via noteUrlInfo(row.url, webHost) — i.e. a xiaohongshu note link on the expected host yielding a note key. Otherwise the row is rejected, preventing untrusted or off-site URLs from entering the output.","triggerScenarios":"A harvested row's url is empty, a relative link, a search/explore redirect, or a non-note xiaohongshu URL (or wrong host), so noteUrlInfo returns no key.","commonSituations":"Promoted/ad cards linking off-site or to campaign pages, xiaohongshu changing note URL format (e.g. new path segments or short-link redirects), or the harvest script capturing the card's click handler target instead of the canonical note href.","solutions":["Re-run the search; ads and odd cards often cause one-off bad URLs.","Update the CLI so noteUrlInfo recognizes the current xiaohongshu note URL format.","Check whether a proxy/webHost override is misconfigured, making trusted hosts untrusted.","Report persistent URL rejections with sample URLs so the trusted-host allowlist can be extended."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function looksLikeNoteUrl(u) {\n  try {\n    const url = new URL(u);\n    return /xiaohongshu\\.com$/.test(url.hostname) &&\n      /(\\/explore\\/|\\/discovery\\/item\\/)/.test(url.pathname);\n  } catch { return false; }\n}\n// filter rows before trusting downstream output","typeGuard":"function isTrustedNoteUrl(u, webHost) {\n  return typeof u === 'string' && new URL(u).hostname === webHost;\n}","tryCatchPattern":"try {\n  const rows = await searchNotes(query);\n} catch (e) {\n  if (String(e.message).includes('expected a trusted note URL')) {\n    console.error('Off-format/off-host note link harvested; retry or update CLI:', e.message);\n    return searchNotes(query);\n  }\n  throw e;\n}","preventionTips":["Filter out promoted/ad cards, which often link off-site.","Keep the CLI updated so noteUrlInfo recognizes current note URL formats.","Verify any webHost override configuration points at the real xiaohongshu host.","Treat this error as protective — never bypass the URL trust check."],"tags":["validation","url","security","scraping","xiaohongshu"],"backgroundTag":"untrusted-url-rejected","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}