puppeteer/puppeteer · error · Error

HTTPRequest is missing _interceptionId needed for Fetch.fail

Error message

HTTPRequest is missing _interceptionId needed for Fetch.failRequest

What it means

Thrown by HTTPRequest._abort() (backing request.abort(errorReason)) when this._interceptionId is undefined. abort() issues Fetch.failRequest with the paused request's id; without interception enabled and the request actually paused, there is nothing to fail. Note this is the same _interceptionId guard as continue/respond.

Source

Thrown at packages/puppeteer-core/src/cdp/HTTPRequest.ts:294

      .send('Fetch.fulfillRequest', {
        requestId: this._interceptionId,
        responseCode: status,
        responsePhrase: STATUS_TEXTS[status],
        responseHeaders: headersArray(responseHeaders),
        body: parsedBody?.base64,
      })
      .catch(error => {
        this.interception.handled = false;
        return handleError(error, (this.frame() as any).logger);
      });
  }

  async _abort(
    errorReason: Protocol.Network.ErrorReason | null,
  ): Promise<void> {
    this.interception.handled = true;
    if (this._interceptionId === undefined) {
      throw new Error(
        'HTTPRequest is missing _interceptionId needed for Fetch.failRequest',
      );
    }
    await this.#client
      .send('Fetch.failRequest', {
        requestId: this._interceptionId,
        errorReason: errorReason || 'Failed',
      })
      .catch(error => {
        return handleError(error, (this.frame() as any).logger);
      });
  }
}

View on GitHub (pinned to d484e21c17)

Solutions

  1. Enable interception with await page.setRequestInterception(true) first.
  2. Call abort() at most once per request and not alongside continue/respond.
  3. Filter out non-interceptable URLs (data:, memory cache) before aborting.
  4. Wrap abort() in try/catch to survive races where the request already completed.

Example fix

// before
page.on('request', req => {
  if (/ads/.test(req.url())) req.abort(); // throws without interception
});

// after
await page.setRequestInterception(true);
page.on('request', req => {
  if (/ads/.test(req.url())) {
    req.abort('blockedbyclient').catch(() => {});
  } else {
    req.continue().catch(() => {});
  }
});
Defensive patterns

Strategy: validation

Validate before calling

await page.setRequestInterception(true);
page.on('request', req => {
  if (req.url().startsWith('data:')) return;
  if (shouldBlock(req.url())) {
    req.abort('blockedbyclient').catch(() => {});
  } else {
    req.continue().catch(() => {});
  }
});

Type guard

function canAbort(req: HTTPRequest): boolean {
  return !req.url().startsWith('data:') && !req.url().startsWith('blob:');
}

Try / catch

try {
  await request.abort('Failed');
} catch (e) {
  if (e instanceof Error && /_interceptionId/.test(e.message)) {
    // nothing to abort — ignore
  } else throw e;
}

Prevention

When it happens

Trigger: Calling request.abort() in a request handler while setRequestInterception(true) was never called; aborting a data:/cached request that bypassed the Fetch domain; aborting after the request was already continued/responsed; calling abort in a listener that runs after another listener already handled the request.

Common situations: Ad-blocking style handlers attached without enabling interception; blocking rules applied too late (after load); multiple listeners racing to handle the same request.

Related errors


AI-assisted analysis of puppeteer/puppeteer@d484e21c17 (2026-08-12). Data as JSON: /api/errors/9a7b7f4d9a9ad84b. Report an issue: GitHub.