apify/crawlee · error · MissingRouteError
Route not found for label '${String(label)}'. You must set u
Error message
Route not found for label '${String(label)}'. You must set up a route for this label or a default route. Use `requestHandler`, `router.addHandler` or `router.addDefaultHandler`. What it means
Router.getHandler looks up the route registered for the request's label (or the default route) and throws MissingRouteError when neither exists. The router refuses to handle requests it has no handler for, rather than silently dropping them.
Source
Thrown at packages/core/src/router.ts:410
* The crawler needs an upper bound up front, before it knows which routes a run will actually hit.
*/
getMaxTimeoutSecs(): number | undefined {
return this.#timeouts.size > 0 ? Math.max(...this.#timeouts.values()) : undefined;
}
/**
* Returns route handler for given label. If no label is provided, the default request handler will be returned.
*/
getHandler(label?: string | symbol): (ctx: Context) => Awaitable<void> {
if (label && this.#routes.has(label)) {
return this.#routes.get(label)!;
}
if (this.#routes.has(defaultRoute)) {
return this.#routes.get(defaultRoute)!;
}
throw new MissingRouteError(
`Route not found for label '${String(label)}'.` +
' You must set up a route for this label or a default route.' +
' Use `requestHandler`, `router.addHandler` or `router.addDefaultHandler`.',
);
}
/**
* Validates `request.userData` against the schema registered for its label (if any), replacing it with
* the parsed value. Throws a {@apilink RequestValidationError} when validation fails.
*/
private async validateRequest(context: Context) {
const label = context.request.label;
const schema = this.getSchema(label);
if (schema) {
context.request.userData = (await validateUserData(
label!,
schema,View on GitHub (pinned to dbe57fb09c)
Solutions
- Register the missing route with router.addHandler(label, handler)
- Add router.addDefaultHandler(handler) as a catch-all
- Verify the label on the failing request exactly matches a registered label (string vs symbol, case)
- Audit queued/persisted requests for labels of removed routes
Example fix
// before
router.addHandler('product', handler);
// request with label 'Product' -> MissingRouteError
// after
router.addHandler('product', handler);
router.addDefaultHandler(defaultHandler); Defensive patterns
Strategy: try-catch
Validate before calling
const label = request.userData?.label;
if (label !== undefined && !registeredLabels.has(label)) {
throw new Error(`No route registered for label: ${String(label)}`);
} Type guard
function hasRoute(router: Router, label: string): boolean {
return router.getHandler; // check via a non-throwing lookup if exposed, else keep a Set of registered labels
} Try / catch
try {
handler = router.getHandler(label);
} catch (err) {
if (err.name === 'MissingRouteError') {
handler = fallbackHandler;
} else throw err;
} Prevention
- Always register a default handler with addDefaultHandler
- Centralize label constants to avoid typos
- When removing a route, purge or remap queued requests using its label
- Log unknown labels during development
When it happens
Trigger: Dispatching a request whose userData.label has no matching addHandler(label, ...) registration and no addDefaultHandler(...) was configured.
Common situations: Typo or casing mismatch between the label used when enqueuing and the one registered; label added in one environment but not another; refactoring removed a handler while old queued requests still reference it; forgetting a default catch-all handler.
Related errors
- `alwaysEnqueue` cannot be used together with a custom `uniqu
- Default route is already defined! / Route for label '${Strin
- The `minConcurrency`/`maxConcurrency`/`initialConcurrency`/`
- The `requestManager` option cannot be used in conjunction wi
- Cannot decide what to purge before running again: `sameDomai
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/4eef2eca426be5c3.
Report an issue: GitHub.