{"record":{"id":"36a0424ea9dbe5db","repo":"DIYgod/RSSHub","slug":"unable-to-access-the-locals-server-function-res","errorCode":null,"errorMessage":"Unable to access the Locals server function (${response.status}).","messagePattern":"Unable to access the Locals server function \\((.+?)\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/locals/feed.ts","lineNumber":364,"sourceCode":"        filter,\n    };\n}\n\nasync function requestServerFunction<T>(session: string, id: string, key: string, args: unknown[]) {\n    const response = await ofetch.raw(`${rootUrl}/_server`, {\n        body: createRequestBody(args),\n        headers: {\n            'Content-Type': 'application/json',\n            ...getRequestHeaders(session),\n            'X-Server-Id': id,\n            'X-Server-Instance': key,\n        },\n        method: 'POST',\n        responseType: 'text',\n    });\n\n    if (!response.ok) {\n        throw new Error(`Unable to access the Locals server function (${response.status}).`);\n    }\n\n    return parseUnknownResponse<T>(response._data!, key);\n}\n\nfunction fetchCommunityInfo(community: string, session: string) {\n    return cache.tryGet(`locals:community:${community}`, async () => {\n        const html = await fetchContentPage(community, session);\n        return extractCommunityInfo(html, community);\n    });\n}\n\nasync function fetchFeedData(communityId: number, community: string, session: string, serverId: string, filter: ContentFilter | undefined, contentType: string | undefined) {\n    const requestFilter = filter ? contentFilterMap[filter] : undefined;\n    const filters = requestFilter ? [requestFilter] : Object.values(contentFilterMap);\n\n    const responses = await Promise.all(\n        filters.map((currentFilter) =>","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/locals/feed.ts#L346-L382","documentation":"Thrown by `requestServerFunction` (lib/routes/locals/feed.ts:363) when the POST to `https://locals.com/_server` returns a non-2xx status. The message embeds `response.status` so the exact HTTP code surfaces. A non-ok response typically means the `X-Server-Id`/`X-Server-Instance` headers are stale, the session cookie is invalid/expired, or Locals rate-limits/blocks the request.","triggerScenarios":"Expired `config.locals.session` cookie (401/403); stale `serverId`/`key` cached from an older bundle (400/500); rate limiting (429); Locals temporarily down (5xx); malformed request body from `createRequestBody`.","commonSituations":"Session cookie rotated by Locals; cache holding an action id from before a deploy; running RSSHub from an IP Locals blocks; clock skew affecting signed payloads.","solutions":["Refresh `config.locals.session` with a current authenticated cookie from a logged-in browser.","Cache-bust `locals:action-ids:<community>` so a fresh `serverId`/`key` is resolved.","Inspect the embedded status: 401/403 -> session; 400 -> stale id or bad body; 429 -> back off / rotate IP.","Retry once after clearing the session-dependent caches; if it persists, the server-function contract changed and the route needs an update."],"exampleFix":"// before: single shot, surfaces only the status\nif (!response.ok) {\n    throw new Error(`Unable to access the Locals server function (${response.status}).`);\n}\n\n// after: include the response body snippet for faster diagnosis\nif (!response.ok) {\n    const snippet = String(response._data ?? '').slice(0, 200);\n    throw new Error(`Unable to access the Locals server function (${response.status}): ${snippet}`);\n}","handlingStrategy":"retry","validationCode":"// Pre-flight: ensure session + action ids look fresh before calling /_server.\nasync function preflightLocalsServer(community: string): Promise<void> {\n    if (!config.locals?.session) throw new Error('Locals session missing');\n    // ensure action ids resolve (throws 325/326 with a clearer message)\n    await resolveActionIds(community, config.locals.session);\n}","typeGuard":"function isServerFunctionOk(r: { ok: boolean; status: number }): boolean { return r.ok; }","tryCatchPattern":"try {\n    return await requestServerFunction<T>(session, id, key, args);\n} catch (e) {\n    const status = (e as Error).message.match(/\\((\\d{3})\\)/)?.[1];\n    if (status === '401' || status === '403') throw new Error('Locals session invalid/expired');\n    if (status === '400') { await cache.delete('locals:action-ids:' + community); /* retry once */ }\n    if (status === '429') { await new Promise(r => setTimeout(r, 5000)); /* retry once */ }\n    throw e;\n}","preventionTips":["Rotate the session cookie before it expires.","Cache action ids with a short TTL and invalidate on 400/500.","Exponential backoff on 429/5xx."],"tags":["network","http-status","session","locals","caching"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}