jackwener/OpenCLI · error · CommandExecutionError
Malformed toutiao recommend row: group_id/source_url mismatc
Error message
Malformed toutiao recommend row: group_id/source_url mismatch for ${JSON.stringify(rawGroupId)}. What it means
buildGroupIdentity cross-checks the explicit group_id against the article id extracted from the source_url path. When both exist and differ, the row is internally inconsistent — trusting either value could produce a wrong canonical link — so it throws this CommandExecutionError naming the conflicting group_id.
Source
Thrown at clis/toutiao/utils.js:234
}
function articleIdFromToutiaoPath(pathname) {
return String(pathname || '').match(/^\/(?:group|article)\/([A-Za-z0-9_-]+)\/?$/)?.[1] || '';
}
function buildGroupIdentity(sourceUrl, rawGroupId) {
const source = firstPartyToutiaoUrl(sourceUrl);
const sourceId = source ? articleIdFromToutiaoPath(source.pathname) : '';
const explicitId = trimOrNull(rawGroupId);
const groupId = explicitId || trimOrNull(sourceId);
if (!groupId) {
throw new CommandExecutionError('Malformed toutiao recommend row: missing group_id and article source_url.');
}
if (!/^[A-Za-z0-9_-]+$/.test(groupId)) {
throw new CommandExecutionError(`Malformed toutiao recommend row: invalid group_id ${JSON.stringify(rawGroupId)}.`);
}
if (sourceId && explicitId && explicitId !== sourceId) {
throw new CommandExecutionError(`Malformed toutiao recommend row: group_id/source_url mismatch for ${JSON.stringify(rawGroupId)}.`);
}
return {
groupId,
url: `https://www.toutiao.com/group/${groupId}/`,
};
}
function formatBehotTime(value) {
const seconds = Number(value);
if (!Number.isFinite(seconds) || seconds <= 0) return null;
return `${new Date(seconds * 1000).toISOString().slice(0, 19)}Z`;
}
/**
* Project a row from the public toutiao channel feed into stable shape.
*
* Sponsored rows are dropped so an agent never reads an ad as editorial
* content; absent counts stay null rather than being coerced to 0.View on GitHub (pinned to 49907e53dc)
Solutions
- Determine which value is correct (fetch both ids and compare content) and fix the row's data.
- Re-derive group_id from source_url by omitting the explicit group_id argument, letting the URL id win.
- Flag mismatched rows for review instead of silently picking one side.
- Add an upstream-consistency check in your ingestion pipeline that runs before buildGroupIdentity.
Example fix
// before buildGroupIdentity(row.source_url, row.group_id); // stale group_id // after buildGroupIdentity(row.source_url, null); // trust source_url-derived id
Defensive patterns
Strategy: validation
Validate before calling
const fromUrl = String(row.source_url || '').replace(/^https?:\/\/[^/]+/, '').match(/^\/(?:group|article)\/([A-Za-z0-9_-]+)\/?$/)?.[1]; if (row.group_id && fromUrl && row.group_id !== fromUrl) flagMismatch(row);
Type guard
const idsConsistent = (row) => { const fromUrl = String(row?.source_url || '').replace(/^https?:\/\/[^/]+/, '').match(/^\/(?:group|article)\/([A-Za-z0-9_-]+)\/?$/)?.[1]; return !row?.group_id || !fromUrl || row.group_id === fromUrl; }; Try / catch
try {
const identity = buildGroupIdentity(row.source_url, row.group_id);
} catch (e) {
if (e instanceof CommandExecutionError && e.message.includes('mismatch')) return quarantineRow(row, e.message);
throw e;
} Prevention
- Prefer deriving the id from source_url (pass null group_id) when the two might disagree.
- Add an ETL assertion that group_id matches the id embedded in source_url.
- Quarantine mismatched rows for manual review instead of guessing.
When it happens
Trigger: A row where group_id='123' but source_url='https://www.toutiao.com/article/456/'; copying a group_id from a different row than its URL; merging datasets where ids and URLs were not kept in sync.
Common situations: Faulty ETL joins mapping ids to the wrong rows; upstream feed republishing an article under a new id while the old group_id remains cached; hand-edited fixtures.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Nowcoder detail returned a different post identity
- Malformed toutiao recommend row: off-domain source_url ${JSO
- Malformed toutiao recommend row: invalid source_url ${JSON.s
- Malformed toutiao recommend row: missing group_id and articl
- Malformed toutiao recommend row: invalid group_id ${JSON.str
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/4ace6a61b21d2813.
Report an issue: GitHub.