{"record":{"id":"052e8f6f925998d0","repo":"jackwener/OpenCLI","slug":"jike-search-api-returned-a-malformed-post","errorCode":null,"errorMessage":"Jike search API returned a malformed post","messagePattern":"Jike search API returned a malformed post","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/jike/search.js","lineNumber":25,"sourceCode":"const DEFAULT_LIMIT = 20;\nconst MAX_PAGES = 50;\n\nasync function fetchSearchPage(page, keyword, loadMoreKey) {\n    const requestBody = {\n        keywords: keyword,\n        limit: PAGE_SIZE,\n        ...(loadMoreKey ? { loadMoreKey } : {}),\n    };\n    const body = await postJikeApi(page, API_PATH, requestBody, 'Jike search API');\n    if (!body || typeof body !== 'object' || body.success !== true || !Array.isArray(body.data)) {\n        throw new CommandExecutionError(`Jike search API failed: ${String(body?.message || 'malformed response')}`);\n    }\n    return body;\n}\n\nfunction mapPost(post) {\n    if (!post || typeof post !== 'object' || typeof post.id !== 'string' || !post.id) {\n        throw new CommandExecutionError('Jike search API returned a malformed post');\n    }\n    const content = typeof post.content === 'string' ? post.content : '';\n    return {\n        id: post.id,\n        author: typeof post.user?.screenName === 'string' ? post.user.screenName : '',\n        content: content.replace(/\\n/g, ' ').slice(0, 120),\n        likes: Number.isFinite(Number(post.likeCount)) ? Number(post.likeCount) : 0,\n        comments: Number.isFinite(Number(post.commentCount)) ? Number(post.commentCount) : 0,\n        time: typeof post.actionTime === 'string' ? post.actionTime : (typeof post.createdAt === 'string' ? post.createdAt : ''),\n        url: `https://web.okjike.com/originalPost/${post.id}`,\n    };\n}\n\nasync function searchPosts(page, keyword, limit) {\n    const rows = [];\n    const seenIds = new Set();\n    const seenCursors = new Set();\n    let loadMoreKey = null;","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/jike/search.js#L7-L43","documentation":"mapPost validates each search result item from the Jike /1.0/search/integrate API before mapping it to an output row. It throws CommandExecutionError when an item is not an object or lacks a non-empty string id, because every row needs a stable id and post URL. This guards against Jike returning unexpected result shapes inside body.data.","triggerScenarios":"A data[] item in the search response is null, a non-object (e.g. a string), or an object whose post.id is missing, not a string, or an empty string. Items with type !== 'ORIGINAL_POST' are skipped before mapPost, so only ORIGINAL_POST entries reach this check.","commonSituations":"Jike changed its search response schema (field renamed from id); an A/B test returns a new result subtype that still reports type 'ORIGINAL_POST' but has no id; partial/corrupted API responses during rate limiting or login state degradation.","solutions":["Re-login to Jike in the browser session (cookie strategy) and retry the search — stale sessions often yield degraded responses.","Reduce --limit and retry to see if a specific post triggers it; if so the post's payload is anomalous.","Update the CLI/package — the mapping may need adjusting for a Jike API schema change.","Log the raw failing item (e.g. JSON.stringify) and file an issue so mapPost can be widened or the field fixed."],"exampleFix":"// before\nfunction mapPost(post) {\n  if (!post || typeof post !== 'object' || typeof post.id !== 'string' || !post.id) {\n    throw new CommandExecutionError('Jike search API returned a malformed post');\n  }\n  ...\n}\n// after: tolerate and skip bad items instead of failing the whole command\nfunction mapPost(post) {\n  if (!post || typeof post !== 'object' || typeof post.id !== 'string' || !post.id) {\n    console.error('skipping malformed post:', JSON.stringify(post));\n    return null;\n  }\n  ...\n}","handlingStrategy":"type-guard","validationCode":"// validate API items before mapping\nfunction isMappedPost(item) {\n  return item && typeof item === 'object' && typeof item.id === 'string' && item.id.length > 0;\n}","typeGuard":"const isPost = (p) => !!p && typeof p === 'object' && typeof p.id === 'string' && !!p.id;","tryCatchPattern":"try {\n  const rows = await runCli(['jike', 'search', keyword]);\n} catch (e) {\n  if (/malformed post/.test(e.message)) {\n    console.error('Jike response schema changed; retry or update CLI');\n    return [];\n  }\n  throw e;\n}","preventionTips":["Keep the CLI updated against Jike API schema changes.","Log raw response items when this error occurs to diagnose the new shape.","Wrap row mapping so one bad item does not fail the whole command.","Ensure the browser session is logged in — degraded sessions yield odd payloads."],"tags":["api","schema-validation","jike"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}