Skip to content

Provide Request instance in onSkippedRequest callback #3801

Description

@vdusek

The onSkippedRequest callback currently receives only the URL string and a reason:

// packages/core/src/enqueue_links/shared.ts
export type SkippedRequestCallback = (args: { url: string; reason: SkippedRequestReason }) => Awaitable<void>;

This means handlers can't access request metadata (notably userData) for skipped requests. In several call-sites the full Request is already available but gets discarded — e.g. in basic-crawler.ts the robots.txt skip has the whole request object yet forwards only request.url, and the limit skip explicitly extracts request.url! before calling handleSkippedRequest.

Proposal

Pass the full Request to the callback instead of the URL string:

export type SkippedRequestCallback = (args: { request: Request; reason: SkippedRequestReason }) => Awaitable<void>;

url is dropped, since it's just request.url. To guarantee a Request is always available, normalize skipped URLs to Request at the choke points (the enqueue path and both extractLinks implementations), the same way the Python PR does. Then thread the Request through the basic-crawler.ts call-sites where it's already available instead of dropping it.

Breaking change

This changes the callback signature, so it lands in the next major (v4). We're intentionally not keeping it backward compatible.

Context

Python parity. This was originally proposed for crawlee-python as a backward-compatible change that dual-dispatched on the type annotation of the callback's first parameter (apify/crawlee-python#1999): annotate it as Request to receive the object, or as str (or leave it unannotated) to keep receiving the URL string.

During review we dropped that approach. The runtime annotation inspection is fragile (for instance, it fails to match when the annotation uses a TYPE_CHECKING import alias), so we decided to do it as a clean breaking change in the next major instead. Tracked for Python in apify/crawlee-python#2007.

For crawlee (JS) we follow the same decision: a clean breaking change, no dual dispatch and no optional request field.

Activity

  1. added
    t-toolingIssues with this label are in the ownership of the tooling team.
    on Jul 1, 2026
  2. added this to the 4.0 milestone on Jul 1, 2026
  3. vdusek commented on Jul 1, 2026

    @vdusek
    ContributorAuthor

    Should we keep both url and request in the callback args, or drop url entirely and pass only the Request? Since this will be introduced probably in v4.

  4. fzzle commented on Jul 20, 2026

    @fzzle

    Really need this. Thanks

  5. B4nan commented on Jul 28, 2026

    @B4nan
    Member

    We might want to use a proxy object here so we dont hydrate the request key unless its actually used (it could increase memory usage for no reason otherwise).

  6. added a commit that references this issue on Aug 28, 2026
    d26a7f6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

t-toolingIssues with this label are in the ownership of the tooling team.

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions