DIYgod/RSSHub · warning · RequestInProgressError

This path is currently fetching, please come back later!

Error message

This path is currently fetching, please come back later!

What it means

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'.

Source

Thrown at lib/middleware/cache.ts:49

    if (!value) {
        isRequesting = !(await cacheModule.globalCache.claim(controlKey, config.cache.requestTimeout));
    }

    if (isRequesting) {
        let retryTimes = process.env.NODE_ENV === 'test' ? 1 : 10;
        let bypass = false;
        while (retryTimes > 0) {
            // eslint-disable-next-line no-await-in-loop
            await new Promise((resolve) => setTimeout(resolve, process.env.NODE_ENV === 'test' ? 3000 : 6000));
            // eslint-disable-next-line no-await-in-loop
            if ((await cacheModule.globalCache.get(controlKey)) !== '1') {
                bypass = true;
                break;
            }
            retryTimes--;
        }
        if (!bypass) {
            throw new RequestInProgressError('This path is currently fetching, please come back later!');
        }
        value = await cacheModule.globalCache.get(key);
    }

    if (value) {
        ctx.status(200);
        ctx.header('RSSHub-Cache-Status', 'HIT');
        ctx.set('data', JSON.parse(value));
        await next();
        return;
    }

    if (isRequesting) {
        // waited out a stale claim without finding a cache entry, take over the fetch
        await cacheModule.globalCache.set(controlKey, '1', config.cache.requestTimeout);
    }

    // let routers control cache

View on GitHub (pinned to bed535e087)

Solutions

  1. Retry the request after a short backoff (this is transient).
  2. Raise config.cache.requestTimeout so the claim outlives slow upstream fetches.
  3. Reduce per-request latency: enable upstream caching, lower route limit, or optimize the route.
  4. Scale the instance so the first fetcher is not CPU-starved behind other heavy routes.

Example fix

// config: give the fetcher longer before peers give up
CACHE_REQUEST_TIMEOUT=120
// client: exponential backoff retry on 503
Defensive patterns

Strategy: retry

Validate before calling

// Nothing to validate pre-call; this is a server-side transient state.
// Client strategy: detect 503 + this message and back off.
const shouldRetry = (status: number, body: string) =>
  status === 503 && /currently fetching/.test(body);

Try / catch

for (const delay of [5, 10, 20]) {
  try { return await fetch(url); }
  catch (e) {
    if (!/currently fetching/.test(String(e?.message ?? ''))) throw e;
    await sleep(delay * 1000);
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12). Data as JSON: /api/errors/ec8c549359f0ca2f. Report an issue: GitHub.