{"record":{"id":"ec8c549359f0ca2f","repo":"DIYgod/RSSHub","slug":"this-path-is-currently-fetching-please-come-back","errorCode":null,"errorMessage":"This path is currently fetching, please come back later!","messagePattern":"This path is currently fetching, please come back later!","errorType":"exception","errorClass":"RequestInProgressError","httpStatus":503,"severity":"warning","filePath":"lib/middleware/cache.ts","lineNumber":49,"sourceCode":"    if (!value) {\n        isRequesting = !(await cacheModule.globalCache.claim(controlKey, config.cache.requestTimeout));\n    }\n\n    if (isRequesting) {\n        let retryTimes = process.env.NODE_ENV === 'test' ? 1 : 10;\n        let bypass = false;\n        while (retryTimes > 0) {\n            // eslint-disable-next-line no-await-in-loop\n            await new Promise((resolve) => setTimeout(resolve, process.env.NODE_ENV === 'test' ? 3000 : 6000));\n            // eslint-disable-next-line no-await-in-loop\n            if ((await cacheModule.globalCache.get(controlKey)) !== '1') {\n                bypass = true;\n                break;\n            }\n            retryTimes--;\n        }\n        if (!bypass) {\n            throw new RequestInProgressError('This path is currently fetching, please come back later!');\n        }\n        value = await cacheModule.globalCache.get(key);\n    }\n\n    if (value) {\n        ctx.status(200);\n        ctx.header('RSSHub-Cache-Status', 'HIT');\n        ctx.set('data', JSON.parse(value));\n        await next();\n        return;\n    }\n\n    if (isRequesting) {\n        // waited out a stale claim without finding a cache entry, take over the fetch\n        await cacheModule.globalCache.set(controlKey, '1', config.cache.requestTimeout);\n    }\n\n    // let routers control cache","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/middleware/cache.ts#L31-L67","documentation":"Thrown as RequestInProgressError by the cache middleware when a cache miss occurs but another request already holds the fetch 'claim' for that key. The current request polls the controlKey up to 10 times (6s apart in prod, 3s/1 retry in test); if the holder never releases within ~60s, this error surfaces. RSSHub maps RequestInProgressError to HTTP 503-ish 'try again later'.","triggerScenarios":"Two or more concurrent requests for the same path+format+limit while the cache is cold; the winning fetch takes longer than (retryCount * interval) because the upstream source is slow or the route does heavy work; config.cache.requestTimeout expires and the claim is held stale.","commonSituations":"Feed reader hammers a freshly-deployed route on cache expiry; upstream site is rate-limited/slow so the first fetch overruns the retry window; Redis claim TTL (requestTimeout) shorter than the slowest realistic fetch.","solutions":["Retry the request after a short backoff (this is transient).","Raise config.cache.requestTimeout so the claim outlives slow upstream fetches.","Reduce per-request latency: enable upstream caching, lower route limit, or optimize the route.","Scale the instance so the first fetcher is not CPU-starved behind other heavy routes."],"exampleFix":"// config: give the fetcher longer before peers give up\nCACHE_REQUEST_TIMEOUT=120\n// client: exponential backoff retry on 503","handlingStrategy":"retry","validationCode":"// Nothing to validate pre-call; this is a server-side transient state.\n// Client strategy: detect 503 + this message and back off.\nconst shouldRetry = (status: number, body: string) =>\n  status === 503 && /currently fetching/.test(body);","typeGuard":null,"tryCatchPattern":"for (const delay of [5, 10, 20]) {\n  try { return await fetch(url); }\n  catch (e) {\n    if (!/currently fetching/.test(String(e?.message ?? ''))) throw e;\n    await sleep(delay * 1000);\n  }\n}","preventionTips":["Set CACHE_REQUEST_TIMEOUT longer than your slowest route fetch.","Stagger feed-reader polls to avoid thundering herds on cache expiry.","Monitor upstream latency so you notice when fetches start overrunning the retry window."],"tags":["cache","concurrency","performance","transient"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}