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
- Read the issues on RequestValidationError and fix the userData to match the schema
- Validate userData with the same schema at the point where requests are created
- Add defaults/optional fields to the schema for legitimately optional data
- 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
- Validate userData at creation time with the same schema the router uses
- Keep producer and consumer schemas in a shared module
- Include the label in userData explicitly and verify it against registered routes
- Test enqueue -> handle round trips in CI
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
- The state conversion for '${persistStateKey}' validated asyn
- StateValidationError(persistStateKey, result.issues)
- StateValidationError(this.#persistStateKey, result.issues)
- Request with GET method cannot have a payload.
- `alwaysEnqueue` cannot be used together with a custom `uniqu
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/c125ed6b43280b08.
Report an issue: GitHub.