{"record":{"id":"614f15ef93f389fb","repo":"jackwener/OpenCLI","slug":"topic-id-does-not-contain-a-readable-main-post","errorCode":null,"errorMessage":"Topic ${id} does not contain a readable main post body.","messagePattern":"Topic (.+?) does not contain a readable main post body\\.","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"error","filePath":"clis/linux-do/topic-content.js","lineNumber":70,"sourceCode":"        frontMatter ? `---\\n${frontMatter}\\n---` : '',\n        params.body.trim(),\n    ].filter(Boolean).join('\\n\\n').trim();\n}\nfunction extractTopicContent(payload, id) {\n    const topic = normalizeTopicPayload(payload);\n    if (!topic) {\n        throw new CommandExecutionError('linux.do returned an unexpected topic payload');\n    }\n    const posts = topic.post_stream?.posts ?? [];\n    const mainPost = posts.find((post) => post.post_number === 1);\n    if (!mainPost) {\n        throw new EmptyResultError('linux-do/topic-content', `Could not find the main post for topic ${id}.`);\n    }\n    const body = typeof mainPost.raw === 'string' && mainPost.raw.trim()\n        ? mainPost.raw.trim()\n        : htmlToMarkdown(mainPost.cooked ?? '');\n    if (!body) {\n        throw new EmptyResultError('linux-do/topic-content', `Topic ${id} does not contain a readable main post body.`);\n    }\n    return {\n        content: buildTopicMarkdownDocument({\n            title: topic.title?.trim() ?? '',\n            author: mainPost.username?.trim() ?? '',\n            likes: typeof mainPost.like_count === 'number' ? mainPost.like_count : undefined,\n            createdAt: toLocalTime(mainPost.created_at ?? ''),\n            url: `${LINUX_DO_HOME}/t/${id}`,\n            body,\n        }),\n    };\n}\nasync function fetchTopicPayload(page, id) {\n    const result = await page.evaluate(`(async () => {\n    try {\n      const res = await fetch('/t/${id}.json?include_raw=true', { credentials: 'include' });\n      let data = null;\n      try {","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linux-do/topic-content.js#L52-L88","documentation":"The library renders the main post from its raw markdown; if raw is empty it falls back to converting the cooked HTML via htmlToMarkdown. If both yield an empty body, there is nothing readable to output, so an EmptyResultError is thrown.","triggerScenarios":"Main post exists but has both empty raw and empty cooked fields — e.g. post content removed/hidden, content behind login not captured by the browser scrape, or a post consisting only of removed embeds.","commonSituations":"Moderator-hidden or deleted-post bodies; linux.do returning posts with redacted content to anonymous/crawler sessions; posts whose body is only an image that failed to render into markdown.","solutions":["Sign in to linux.do in the browser session so full post bodies are served.","Verify the post has visible content in the web UI.","Check whether the post was hidden/deleted by moderators.","Extend the pipeline to fall back to another field (e.g. cooked HTML as-is) if htmlToMarkdown output is empty."],"exampleFix":"// before\nconst body = typeof mainPost.raw === 'string' && mainPost.raw.trim()\n  ? mainPost.raw.trim()\n  : htmlToMarkdown(mainPost.cooked ?? '');\n// after\nconst body = (typeof mainPost.raw === 'string' && mainPost.raw.trim())\n  ? mainPost.raw.trim()\n  : htmlToMarkdown(mainPost.cooked ?? '') || (mainPost.cooked ?? '').trim();","handlingStrategy":"fallback","validationCode":"const raw = mainPost?.raw?.trim();\nconst cooked = mainPost?.cooked?.trim();\nif (!raw && !cooked) throw new Error(`Topic ${id} has no readable body`);","typeGuard":"const hasReadableBody = (post) =>\n  Boolean((typeof post?.raw === 'string' && post.raw.trim()) || (typeof post?.cooked === 'string' && post.cooked.trim()));","tryCatchPattern":"try {\n  return extractTopicContent(payload, id);\n} catch (e) {\n  if (e instanceof EmptyResultError && /readable main post body/.test(e.message)) {\n    return { id, content: '' }; // degrade gracefully\n  } else throw e;\n}","preventionTips":["Sign in so post bodies are not redacted","Fall back to cooked HTML when raw is empty","Skip hidden/deleted posts in batch jobs"],"tags":["discourse","empty-result","content-extraction"],"backgroundTag":"empty-response-body","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}