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

  1. Register the missing route with router.addHandler(label, handler)
  2. Add router.addDefaultHandler(handler) as a catch-all
  3. Verify the label on the failing request exactly matches a registered label (string vs symbol, case)
  4. 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

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


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