apify/crawlee · error · RequestValidationError

RequestValidationError(label, result.issues)

Error message

RequestValidationError(label, result.issues)

What it means

validateUserData runs the request's user data through the route's Standard Schema and throws RequestValidationError (including the label and the validation issues) when the data does not conform. It backs validateRequestUserData and validateRequest, so invalid userData is caught before a handler runs.

Source

Thrown at packages/core/src/router.ts:107

export async function validateUserData(
    label: string | symbol,
    schema: StandardSchemaV1,
    userData: unknown,
): Promise<Dictionary> {
    const { label: _label, ...rest } = (userData ?? {}) as Dictionary;

    // `label` is a Crawlee-managed key that lives inside `userData`, so validating it is opt-in: we validate
    // without it first, letting schemas that don't describe it pass (including `.strict()` ones). A schema that
    // *does* declare `label` reports an issue for the now-missing key — so we re-validate with it included,
    // honouring the declaration. Unlike `userData.__crawlee`, `label` is enumerable, so schemas do see it.
    let result = await schema['~standard'].validate(rest);

    if (result.issues?.some(isLabelIssue)) {
        result = await schema['~standard'].validate({ ...rest, label });
    }

    if (result.issues) {
        throw new RequestValidationError(label, result.issues);
    }

    // Restore the label so it survives schemas that strip undeclared keys.
    return { ...(result.value as Dictionary), label };
}

/**
 * The set of labels accepted by {@apilink Router.addHandler}. When the router declares a concrete
 * route map (e.g. `{ PRODUCT: ...; CATEGORY: ... }`), only those labels (plus symbols) are
 * allowed — unknown labels become a compile-time error. When the map is left open (the default
 * `Record<string, ...>`), any string or symbol label is accepted, preserving the original behaviour.
 */
export type RouterLabel<Routes extends Record<keyof Routes, Dictionary>> = string extends keyof Routes
    ? string | symbol
    : (keyof Routes & string) | symbol;

export interface RouterHandler<
    Context extends RestrictedCrawlingContext = CrawlingContext,

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Read the issues on RequestValidationError and fix the userData to match the schema
  2. Validate userData with the same schema at the point where requests are created
  3. Add defaults/optional fields to the schema for legitimately optional data
  4. Ensure the label is set correctly — the validator re-validates with the label when the label field itself fails

Example fix

// before
await crawler.addRequests([{ url, userData: { label: 'detail', id } }]);
// after
const userData = detailSchema.parse({ label: 'detail', id });
await crawler.addRequests([{ url, userData }]);
Defensive patterns

Strategy: validation

Validate before calling

const check = schema['~standard'].validate(userData);
if ((await check).issues) throw new RequestValidationError(label, (await check).issues);

Type guard

function isValidUserData<T>(v: unknown, schema: StandardSchema<T>): v is T {
  return !schema['~standard'].validate(v).issues;
}

Try / catch

try {
  await crawler.run(requests);
} catch (err) {
  if (err instanceof RequestValidationError) {
    console.error(`Invalid userData for label ${err.label}:`, err.issues);
  } else throw err;
}

Prevention

When it happens

Trigger: Enqueuing or dispatching a request whose userData fails the schema declared for its label, e.g. missing required fields or wrong types in crawler.addRequests([...]) data.

Common situations: Changing a handler's expected userData schema without updating producers; JSON payloads from queues/APIs missing fields; label-specific schemas applied to requests created elsewhere.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30). Data as JSON: /api/errors/c125ed6b43280b08. Report an issue: GitHub.