DIYgod/RSSHub · error · Error
Failed to fetch thread data
Error message
Failed to fetch thread data
What it means
The Threads route scrapes the profile HTML, parses each <script data-sjs> blob as JSON, and runs a JSONPath for $..thread_items[0]. If none of the script blobs yield thread_items, threadsData stays null and the route throws 'Failed to fetch thread data'. The cause is upstream: Threads did not embed the expected data (markup changed, login wall, rate limit, or empty/private profile).
Source
Thrown at lib/routes/threads/index.ts:96
let threadsData: ThreadItem[] | null = null;
for (const el of document.querySelectorAll('script[data-sjs]')) {
try {
const data = JSONPath({
path: '$..thread_items[0]',
json: JSON.parse(el.textContent || ''),
});
if (data?.length > 0) {
threadsData = data as ThreadItem[];
break;
}
} catch {
// Skip invalid JSON
}
}
if (!threadsData) {
throw new Error('Failed to fetch thread data');
}
debugJson.profileId = userId;
debugJson.response = { response: threadsData };
const userData: ThreadUser = threadsData[0]?.post?.user || { username: user, profile_pic_url: '' };
const items = threadsData
.filter((item) => user === item.post.user?.username)
.map((item) => ({
author: user,
title: buildContent(item, options).title,
description: buildContent(item, options).description,
pubDate: parseDate(item.post.taken_at, 'X'),
link: threadUrl(item.post.code),
}));
debugJson.items = items;View on GitHub (pinned to bed535e087)
Solutions
- Open profileUrl(user) in a browser with the same UA and confirm thread_items is present in the data-sjs scripts; if not, update the JSONPath to the new location.
- If a login/consent page is served, rotate the egress IP or adjust headers to look less like a scraper.
- Confirm the username is correct and the profile is public and non-empty.
- Add a logged slice of the response when threadsData stays null so future markup changes are diagnosable.
Example fix
// before
if (!threadsData) {
throw new Error('Failed to fetch thread data');
}
// after: include diagnostic context
if (!threadsData) {
const scriptCount = document.querySelectorAll('script[data-sjs]').length;
const looksLikeLogin = /login|challenge/i.test(response);
throw new Error(`Failed to fetch thread data for "${user}" (data-sjs scripts: ${scriptCount}, login-wall suspected: ${looksLikeLogin})`);
} Defensive patterns
Strategy: retry
Validate before calling
function profileEmbedsThreads(html: string): boolean {
return /data-sjs/.test(html) && /thread_items/.test(html);
}
// fetch the profile HTML first; if !profileEmbedsThreads(html), back off or rotate IP before parsing Type guard
function profileHtmlHasThreadData(html: string): boolean {
return /<script[^>]*data-sjs/.test(html) && /thread_items/.test(html);
} Try / catch
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await buildThreadsFeed(user);
} catch (e) {
if (e instanceof Error && /Failed to fetch thread data/.test(e.message)) {
await sleep(10_000 * (attempt + 1)); // likely rate-limit/markup drift
continue;
}
throw e;
}
}
throw new Error(`Threads feed unavailable for "${user}" after retries`); Prevention
- Keep the JSONPath ($..thread_items[0]) in sync with Threads' current embedded shape.
- Rotate egress IPs and keep the iOS-Safari UA current to avoid login walls.
- Confirm the username is correct and the profile is public before subscribing.
- Log a slice of the HTML when thread_items is absent to detect markup changes fast.
When it happens
Trigger: The Threads profile page no longer embeds thread_items in a data-sjs script; the request was served a login/consent page instead of the SPA shell; the profile is private or has no threads; the JSONPath expression no longer matches the new data shape.
Common situations: Threads changes its embedded JSON structure (frequent for anti-scraping sites); the hardcoded iOS-Safari User-Agent gets blocked; rate-limiting after polling; profile handle typo.
Related errors
- LSD token not found
- Unable to locate stores data for region ${region}
- 无法解析页面数据,请检查漫画 ID 是否正确或页面结构是否变动
- 无法解析页面 Props 数据
- 无法解析页面 HTML 数据,可能触发了反爬策略或页面结构巨变
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/d2931202e35365fa.
Report an issue: GitHub.