{"record":{"id":"c49bd98c6eb39041","repo":"DIYgod/RSSHub","slug":"lsd-token-not-found","errorCode":null,"errorMessage":"LSD token not found","messagePattern":"LSD token not found","errorType":"exception","errorClass":"NotFoundError","httpStatus":404,"severity":"error","filePath":"lib/routes/threads/utils.ts","lineNumber":37,"sourceCode":"            Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',\n            'Accept-Encoding': 'gzip, br',\n            'Accept-Language': 'zh-CN,zh;q=0.9',\n            'Cache-Control': 'no-cache',\n            Pragma: 'no-cache',\n            'Sec-Fetch-Dest': 'document',\n            'Sec-Fetch-Mode': 'navigate',\n            'Sec-Fetch-Site': 'none',\n            'Sec-Fetch-User': '?1',\n            'Upgrade-Insecure-Requests': '1',\n        },\n    });\n\n    const $ = load(response);\n    const data = $('script:contains(\"LSD\"):first').text();\n    const lsd = data.match(/\"LSD\",\\[\\],\\{\"token\":\"([\\w@-]+)\"\\},/)?.[1];\n\n    if (!lsd) {\n        throw new NotFoundError('LSD token not found');\n    }\n\n    return { lsd };\n};\n\nconst getUserId = async (user: string): Promise<string> => {\n    const result = await cache.tryGet(`threads:userId:${user}`, async () => {\n        const response = await ofetch(profileUrl(user), {\n            headers: {\n                'User-Agent': USER_AGENT,\n                Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',\n                'Accept-Encoding': 'gzip, br',\n                'Accept-Language': 'zh-CN,zh;q=0.9',\n                'Cache-Control': 'no-cache',\n                Pragma: 'no-cache',\n                'Sec-Fetch-Dest': 'document',\n                'Sec-Fetch-Mode': 'navigate',\n                'Sec-Fetch-Site': 'none',","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/threads/utils.ts#L19-L55","documentation":"Thrown by Threads (Meta) profile scraping when the regex cannot extract the LSD (Login Session Data) CSRF token from the profile HTML. The token is required for authenticated GraphQL calls to Threads' internal API. A NotFoundError indicates the page markup no longer contains the expected `\"LSD\",[],{\"token\":\"...\"}` pattern.","triggerScenarios":"The ofetch to https://www.threads.com/@{user} returns HTML whose inline scripts do not contain the LSD token string, so the regex match on line 34 yields undefined and line 36-37 throws. This happens when Threads changes its bundler output, serves a login-wall/consent page, or rate-limits the request.","commonSituations":"Threads rotates its client bundle and the LSD token key/shape changes; an unsupported region gets redirected to an interstitial; the mobile User-Agent is fingerprinted and served a different page; ofetch follows a redirect to a login page.","solutions":["Verify the profile URL still returns the expected HTML by reproducing the ofetch with the same headers in isolation.","Update the regex on line 34 to match Threads' current LSD token serialization (open the page, search the bundle for `LSD`).","If Threads now gates the page behind login, switch the route to an authenticated flow or a different API surface.","Add a retry with a different User-Agent / locale to dodge interstitials."],"exampleFix":"// before\nconst lsd = data.match(/\"LSD\",\\[\\],\\{\"token\":\"([\\w@-]+)\"\\},/)?.[1];\n// after (loosen the matcher to current bundle shape)\nconst lsd = data.match(/\"LSD\"[^}]*\"token\":\"([\\w@-]+)\"/)?.[1];","handlingStrategy":"try-catch","validationCode":"const hasLsdShape = (html: string) => /\"LSD\"[^}]*\"token\":\"[\\w@-]+\"/.test(html);\nconst html = await ofetch(profileUrl(user), { headers });\nif (!hasLsdShape(html)) throw new Error('Threads page lacks LSD token — bundle changed or interstitial served');","typeGuard":"const isLsdToken = (v: unknown): v is string => typeof v === 'string' && /^[\\w@-]+$/.test(v);","tryCatchPattern":"try { const { lsd } = await extractTokens(user); /* use lsd */ }\ncatch (e) { if (e instanceof NotFoundError) { /* fall back to alt API or surface 503 */ } else throw e; }","preventionTips":["Monitor the Threads bundle for LSD token shape changes","Pin a User-Agent known to return the full HTML","Detect login-wall HTML before parsing"],"tags":["scraping","threads","csrf-token","html-parsing","anti-crawler"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}