{"record":{"id":"835c086334e77359","repo":"DIYgod/RSSHub","slug":"cannot-get-the-redirect-link","errorCode":null,"errorMessage":"Cannot get the redirect link","messagePattern":"Cannot get the redirect link","errorType":"exception","errorClass":"Error","httpStatus":503,"severity":"error","filePath":"lib/routes/t66y/post.ts","lineNumber":67,"sourceCode":"    },\n    name: '帖子跟踪',\n    maintainers: ['cnzgray'],\n    handler,\n    description: `::: tip\n帖子 id 查找办法:\n\n打开想跟踪的帖子，比如：\\`https://t66y.com/htm_data/20/1811/3286088.html\\` 其中 \\`3286088\\` 就是帖子 id。\n:::`,\n};\n\nasync function handler(ctx) {\n    const tid = ctx.req.param('tid') as string;\n    const { data: response } = await got(`${baseUrl}/read.php?tid=${tid}`);\n    // 跟踪重定向\n    let $ = load(response);\n    const redirect = $('a:last-child').attr('href');\n    if (!redirect) {\n        throw new Error('Cannot get the redirect link');\n    }\n\n    const { data: redirectedResponse, url: link } = await got(new URL(redirect, baseUrl).href);\n    $ = load(redirectedResponse);\n\n    const firstPage = parseItems(tid, $);\n\n    const pageSize = $('.w70 input').eq(0).attr('value')?.split('/', 2)[1];\n    let pageUrls: string[] = [];\n    if (pageSize) {\n        const length = Number.parseInt(pageSize);\n        pageUrls = Array.from({ length }, (_, i) => `${baseUrl}/read.php?tid=${tid}&page=${i + 1}`).slice(1);\n    }\n\n    // 请求帖子\n    const nextPages = pageSize\n        ? await Promise.all(\n              pageUrls.map((url) =>","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/t66y/post.ts#L49-L85","documentation":"Thrown by t66y/post.ts:67 via `throw new Error(...)` (NOT an InvalidParameterError) when $('a:last-child').attr('href') is falsy on the first response. t66y serves an interstitial redirect page at /read.php?tid=... containing an <a> link the route follows to reach the real thread; if that anchor is absent, the redirect cannot be resolved. The route is flagged antiCrawler: true, so the most common real cause is being blocked or served a non-redirect page rather than a genuine markup change.","triggerScenarios":"t66y returns a Cloudflare/WAF block page instead of the redirect interstitial; the tid is invalid/deleted and the page shows an error with no link; the site changed its redirect markup so a:last-child no longer matches; t66y is down or geo-blocked.","commonSituations":"Anti-bot block (rate limiting from the RSSHub IP); expired/removed thread; geographic restriction; markup change after a site update.","solutions":["Retry after a short delay — transient anti-bot blocks often clear.","Open https://t66y.com/read.php?tid=<tid> in a browser to see the actual page returned (block page, error, or changed markup).","If the markup changed, update the selector (currently a:last-child) in post.ts.","Confirm the tid corresponds to a live thread."],"exampleFix":"// before\nconst redirect = $('a:last-child').attr('href');\nif (!redirect) {\n    throw new Error('Cannot get the redirect link');\n}\n// after (fail with richer context + try alternate selectors)\nconst redirect = $('a:last-child').attr('href') ?? $('a.continue').attr('href');\nif (!redirect) {\n    throw new Error(`Cannot get the redirect link for tid=${tid} (possible anti-bot block or markup change)`);\n}","handlingStrategy":"retry","validationCode":"// Pre-validate the tid shape; the real cause is usually server-side.\nfunction isPlausibleTid(tid: string): boolean {\n  return typeof tid === 'string' && /^\\d+$/.test(tid) && tid.length > 0;\n}","typeGuard":"function isTid(tid: string): tid is string {\n  return typeof tid === 'string' && /^\\d+$/.test(tid);\n}","tryCatchPattern":"// t66y blocks are often transient — retry with backoff.\nasync function fetchThread(tid: string, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await fetchFeed(`/t66y/post/${tid}`);\n    } catch (e) {\n      const last = i === attempts - 1;\n      if (last || !(e instanceof Error) || !/redirect link/i.test(e.message)) throw e;\n      await wait(Math.pow(2, i) * 1000); // host-provided wait primitive\n    }\n  }\n}","preventionTips":["Confirm the tid points to a live thread by opening https://t66y.com/read.php?tid=<tid> first.","Treat this as transient anti-bot block first, markup change second.","If the redirect selector changed, update $('a:last-child') in post.ts rather than retrying indefinitely."],"tags":["network","anti-crawler","markup-change","t66y","upstream-change","wrong-error-type"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}