{"record":{"id":"e434aaba61406dd9","repo":"jackwener/OpenCLI","slug":"malformed-toutiao-recommend-row-invalid-group-id","errorCode":null,"errorMessage":"Malformed toutiao recommend row: invalid group_id ${JSON.stringify(rawGroupId)}.","messagePattern":"Malformed toutiao recommend row: invalid group_id (.+?)\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/toutiao/utils.js","lineNumber":231,"sourceCode":"        if (error instanceof CommandExecutionError) throw error;\n        throw new CommandExecutionError(`Malformed toutiao recommend row: invalid source_url ${JSON.stringify(raw)}.`);\n    }\n}\n\nfunction articleIdFromToutiaoPath(pathname) {\n    return String(pathname || '').match(/^\\/(?:group|article)\\/([A-Za-z0-9_-]+)\\/?$/)?.[1] || '';\n}\n\nfunction buildGroupIdentity(sourceUrl, rawGroupId) {\n    const source = firstPartyToutiaoUrl(sourceUrl);\n    const sourceId = source ? articleIdFromToutiaoPath(source.pathname) : '';\n    const explicitId = trimOrNull(rawGroupId);\n    const groupId = explicitId || trimOrNull(sourceId);\n    if (!groupId) {\n        throw new CommandExecutionError('Malformed toutiao recommend row: missing group_id and article source_url.');\n    }\n    if (!/^[A-Za-z0-9_-]+$/.test(groupId)) {\n        throw new CommandExecutionError(`Malformed toutiao recommend row: invalid group_id ${JSON.stringify(rawGroupId)}.`);\n    }\n    if (sourceId && explicitId && explicitId !== sourceId) {\n        throw new CommandExecutionError(`Malformed toutiao recommend row: group_id/source_url mismatch for ${JSON.stringify(rawGroupId)}.`);\n    }\n    return {\n        groupId,\n        url: `https://www.toutiao.com/group/${groupId}/`,\n    };\n}\n\nfunction formatBehotTime(value) {\n    const seconds = Number(value);\n    if (!Number.isFinite(seconds) || seconds <= 0) return null;\n    return `${new Date(seconds * 1000).toISOString().slice(0, 19)}Z`;\n}\n\n/**\n * Project a row from the public toutiao channel feed into stable shape.","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/toutiao/utils.js#L213-L249","documentation":"buildGroupIdentity requires the resolved group id to match /^[A-Za-z0-9_-]+$/. After falling back from explicit group_id to the source_url-derived article id, any id containing other characters (spaces, slashes, CJK, punctuation) fails this check and throws this CommandExecutionError, because the id is used to construct https://www.toutiao.com/group/<id>/.","triggerScenarios":"A group_id containing URL-encoded or raw special characters, whitespace, or full-width characters; a source_url whose captured path segment includes unexpected characters (e.g. '/article/abc.def/').","commonSituations":"Upstream changing id formats (numeric ids becoming prefixed or composite ids); double-encoded ids like '%7C' left un-decoded; whitespace or trailing '/' fragments captured by an over-broad regex in custom preprocessing.","solutions":["Sanitize/decode the id first (decodeURIComponent, trim) and re-check it matches [A-Za-z0-9_-]+ before calling.","Verify against the current Toutiao id format — if ids changed upstream, update the validation pattern.","Drop rows with invalid ids and report them; do not attempt to force-strip invalid characters (yields dead links).","If you construct ids yourself, validate with /^[A-Za-z0-9_-]+$/.test(id) before invoking."],"exampleFix":"// before\nbuildGroupIdentity(url, decodeURIComponent(rawGroupId));\n// after\nconst id = decodeURIComponent(rawGroupId).trim();\nif (!/^[A-Za-z0-9_-]+$/.test(id)) throw new SkipRowError(id);\nbuildGroupIdentity(url, id);","handlingStrategy":"validation","validationCode":"const id = String(rawGroupId || '').trim();\nif (!/^[A-Za-z0-9_-]+$/.test(id)) throw new TypeError(`invalid group_id format: ${JSON.stringify(rawGroupId)}`);","typeGuard":"const isValidGroupId = (v) => typeof v === 'string' && /^[A-Za-z0-9_-]+$/.test(v.trim());","tryCatchPattern":"try {\n  const identity = buildGroupIdentity(url, rawGroupId);\n} catch (e) {\n  if (e instanceof CommandExecutionError && e.message.includes('invalid group_id')) return dropRow(rawGroupId);\n  throw e;\n}","preventionTips":["decodeURIComponent + trim ids extracted from URLs before use.","Validate against /^[A-Za-z0-9_-]+$/ in your own pipeline first.","Re-check the pattern if Toutiao changes id formats upstream."],"tags":["data-integrity","format-validation","toutiao"],"backgroundTag":"invalid-identifier-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}