jackwener/OpenCLI · error · CommandExecutionError
Unexpected Xiaohongshu search harvest row ${index + 1} autho
Error message
Unexpected Xiaohongshu search harvest row ${index + 1} author URL; expected a trusted profile URL. What it means
requireTrustedHarvestRow validates each row returned by the in-browser harvest script after a Xiaohongshu search scrape. When a row's author_url does not pass isTrustedAuthorUrl (i.e. it is not a profile URL on the trusted xiaohongshu web host), the function throws this CommandExecutionError. This guards against poisoned or malformed harvested data being trusted downstream.
Source
Thrown at clis/xiaohongshu/search.js:304
}
catch {
return false;
}
}
function requireTrustedHarvestRow(row, index, webHost) {
if (!row || typeof row !== 'object' || Array.isArray(row)) {
throw new CommandExecutionError(`Unexpected Xiaohongshu search harvest row ${index + 1} shape; expected an object.`);
}
for (const field of ['title', 'author', 'likes', 'url', 'author_url']) {
if (typeof row[field] !== 'string') {
throw new CommandExecutionError(`Unexpected Xiaohongshu search harvest row ${index + 1} shape; expected string ${field}.`);
}
}
if (!noteUrlInfo(row.url, webHost).key) {
throw new CommandExecutionError(`Unexpected Xiaohongshu search harvest row ${index + 1} URL; expected a trusted note URL.`);
}
if (!isTrustedAuthorUrl(row.author_url, webHost)) {
throw new CommandExecutionError(`Unexpected Xiaohongshu search harvest row ${index + 1} author URL; expected a trusted profile URL.`);
}
return row;
}
function requireHarvestPayload(payload, webHost) {
const result = unwrapEvaluateResult(payload);
const diag = result?.diag;
if (!result || typeof result !== 'object' || Array.isArray(result) || !Array.isArray(result.rows) ||
!diag || typeof diag !== 'object' || Array.isArray(diag) ||
typeof diag.securityBlock !== 'boolean' || typeof diag.stopReason !== 'string' ||
!Number.isFinite(diag.scrollHeight) || diag.scrollHeight < 0 ||
!Number.isFinite(diag.clientHeight) || diag.clientHeight < 0 ||
!Number.isSafeInteger(diag.cardCount) || diag.cardCount < 0 ||
!(diag.feedClientHeight === null || (Number.isFinite(diag.feedClientHeight) && diag.feedClientHeight >= 0)) ||
!Number.isSafeInteger(diag.distinctCardTops) || diag.distinctCardTops < 0) {
throw new CommandExecutionError('Unexpected Xiaohongshu search harvest payload shape; expected rows plus typed diagnostics.');
}
result.rows = result.rows.map((row, index) => requireTrustedHarvestRow(row, index, webHost));
return result;View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the harvested row's author_url in the payload and confirm it is an absolute https URL on the trusted xiaohongshu web host pointing to a profile page
- Update isTrustedAuthorUrl / noteUrlInfo host allowlist if Xiaohongshu changed its profile URL format, and add the new format to the trust check
- Re-run the search after dismissing any login, captcha, or interstitial overlay that corrupts harvested cards
- Log the offending row (index and author_url) before throwing to aid diagnosis, then harden the harvest script to skip rows without a trusted author link
Example fix
// before
if (!isTrustedAuthorUrl(row.author_url, webHost)) {
throw new CommandExecutionError(`Unexpected Xiaohongshu search harvest row ${index + 1} author URL; expected a trusted profile URL.`);
}
// after
if (!isTrustedAuthorUrl(row.author_url, webHost)) {
console.warn('skipping row with untrusted author_url:', row.author_url);
return null; // filter out instead of aborting the whole harvest
} Defensive patterns
Strategy: validation
Validate before calling
function isTrustedAuthorUrlForCaller(url) {
try {
const u = new URL(url, 'https://www.xiaohongshu.com');
return (u.hostname === 'www.xiaohongshu.com' || u.hostname === 'xiaohongshu.com') && /\/user\/profile\//.test(u.pathname);
} catch { return false; }
}
// check payload.rows.every((r, i) => isTrustedAuthorUrlForCaller(r.author_url)) before consuming rows Type guard
const hasTrustedAuthor = (row) => typeof row?.author_url === 'string' && /^https:\/\/(www\.)?xiaohongshu\.com\/user\/profile\//.test(row.author_url);
Try / catch
try {
const payload = requireHarvestPayload(raw, webHost);
} catch (e) {
if (e.message.includes('expected a trusted profile URL')) {
// drop or quarantine the offending row; re-run the harvest if rows are missing
} else throw e;
} Prevention
- Validate author_url shape on raw payloads before processing
- Keep the host/path allowlist in sync with Xiaohongshu's current profile URL format
- Dismiss login/captcha overlays before harvesting so cards render normally
- Log untrusted rows instead of silently failing the entire harvest
When it happens
Trigger: The browser-side evaluate script returned a row whose author_url is missing, empty, relative, points at a non-profile page, or is on an unexpected host; row 4841-style payload passes URL validation but the author link failed the trust check.
Common situations: Xiaohongshu DOM changes so author links are rendered differently (e.g. relative '/user/profile/...' paths instead of absolute URLs), a card lacks an author link (anonymous/deleted account), or a bot-check/interstitial page injects unexpected markup that the harvest script captures as a row.
Related errors
- Unexpected Xiaohongshu search harvest row ${index + 1} shape
- Unexpected Xiaohongshu search harvest row ${index + 1} shape
- Unexpected Xiaohongshu search harvest row ${index + 1} URL;
- ${label} did not include a stable numeric id.
- ${label} did not include a stable text value.
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/3854d4ba3e2b96bf.
Report an issue: GitHub.