jackwener/OpenCLI · error · CommandExecutionError

linux.do returned an unexpected topic payload

Error message

linux.do returned an unexpected topic payload

What it means

extractTopicContent calls normalizeTopicPayload on the fetched topic JSON; if the payload cannot be normalized into a recognizable topic object (null/undefined result), it throws a CommandExecutionError. This guards against feeding malformed or non-topic data into the extraction pipeline.

Source

Thrown at clis/linux-do/topic-content.js:59

        if (typeof value === 'number') {
            frontMatterLines.push(`${key}: ${value}`);
        }
        else {
            // Quote strings that could be misinterpreted by YAML parsers
            const needsQuote = /[#{}[\],&*?|>!%@`'"]/.test(value) || /: /.test(value) || /:$/.test(value) || value.includes('\n');
            frontMatterLines.push(`${key}: ${needsQuote ? `'${value.replace(/'/g, "''")}'` : value}`);
        }
    }
    const frontMatter = frontMatterLines.join('\n');
    return [
        frontMatter ? `---\n${frontMatter}\n---` : '',
        params.body.trim(),
    ].filter(Boolean).join('\n\n').trim();
}
function extractTopicContent(payload, id) {
    const topic = normalizeTopicPayload(payload);
    if (!topic) {
        throw new CommandExecutionError('linux.do returned an unexpected topic payload');
    }
    const posts = topic.post_stream?.posts ?? [];
    const mainPost = posts.find((post) => post.post_number === 1);
    if (!mainPost) {
        throw new EmptyResultError('linux-do/topic-content', `Could not find the main post for topic ${id}.`);
    }
    const body = typeof mainPost.raw === 'string' && mainPost.raw.trim()
        ? mainPost.raw.trim()
        : htmlToMarkdown(mainPost.cooked ?? '');
    if (!body) {
        throw new EmptyResultError('linux-do/topic-content', `Topic ${id} does not contain a readable main post body.`);
    }
    return {
        content: buildTopicMarkdownDocument({
            title: topic.title?.trim() ?? '',
            author: mainPost.username?.trim() ?? '',
            likes: typeof mainPost.like_count === 'number' ? mainPost.like_count : undefined,
            createdAt: toLocalTime(mainPost.created_at ?? ''),

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Ensure you are signed in to linux.do in the controlling browser session (`browser sign-in` flow).
  2. Verify the topic ID exists and is publicly viewable.
  3. Log the raw payload returned by fetchTopicPayload to inspect what linux.do actually sent.
  4. Retry later — the site may be serving an interstitial or outage.

Example fix

// before
const payload = await fetchTopicPayload(page, 12345);
const content = extractTopicContent(payload, 12345);
// after
const payload = await fetchTopicPayload(page, 12345);
if (!payload?.id) {
  throw new Error('Topic payload unusable — check linux.do session in browser');
}
const content = extractTopicContent(payload, 12345);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!payload || typeof payload !== 'object' || !payload.id) {
  throw new Error('Topic payload is not a valid topic object');
}

Type guard

const isTopicPayload = (p) =>
  Boolean(p && typeof p === 'object' && typeof p.id === 'number' && Array.isArray(p.post_stream?.posts));

Try / catch

try {
  return extractTopicContent(payload, id);
} catch (e) {
  if (e instanceof CommandExecutionError && /unexpected topic payload/.test(e.message)) {
    console.error('linux.do did not return topic JSON — check your browser session.');
  } else throw e;
}

Prevention

When it happens

Trigger: fetchTopicPayload returns a body that, after normalization, is not a topic object — e.g. the JSON endpoint returns an HTML login page, an error object, a deleted/hidden topic response, or null data.

Common situations: Expired or missing browser session so linux.do returns a login/error page; deleted or restricted topic; scraping/captcha interstitial; linux.do API schema change.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/8b5d7e62962b12dd. Report an issue: GitHub.