apify/crawlee · error · Error
Default route is already defined! / Route for label '${Strin
Error message
Default route is already defined! / Route for label '${String(label)}' is already defined! What it means
Router.validate (used by addHandler and addDefaultHandler) throws when a route for the given label already exists. The special label for the default route produces the 'Default route is already defined!' message; any other duplicate label produces 'Route for label X is already defined!'. Duplicate registration is treated as a programming error rather than an implicit override.
Source
Thrown at packages/core/src/router.ts:443
if (schema) {
context.request.userData = (await validateUserData(
label!,
schema,
context.request.userData,
)) as GetUserDataFromRequest<Context['request']>;
}
}
/**
* Throws when the label already exists in our registry.
*/
private validate(label: string | symbol) {
if (this.#routes.has(label)) {
const message =
label === defaultRoute
? `Default route is already defined!`
: `Route for label '${String(label)}' is already defined!`;
throw new Error(message);
}
}
/**
* Creates new router instance. This instance can then serve as a `requestHandler` of your crawler.
*
* ```ts
* import { Router, CheerioCrawler, CheerioCrawlingContext } from 'crawlee';
*
* const router = Router.create<CheerioCrawlingContext>();
* router.addHandler('label-a', async (ctx) => {
* ctx.log.info('...');
* });
* router.addDefaultHandler(async (ctx) => {
* ctx.log.info('...');
* });
*
* const crawler = new CheerioCrawler({View on GitHub (pinned to dbe57fb09c)
Solutions
- Register each label only once — guard router setup so it runs a single time
- If overriding is intended, recreate the Router or restructure so the new handler replaces the old at setup time
- Use distinct labels for distinct handlers
- Check for duplicate module initialization (hot reload, multiple entry points)
Example fix
// before
router.addHandler('detail', handlerA);
router.addHandler('detail', handlerB); // throws
// after
router.addHandler('detail', handlerA);
router.addHandler('list', handlerB); Defensive patterns
Strategy: try-catch
Validate before calling
if (registered.has(label)) return; // skip duplicate registration
Try / catch
try {
router.addHandler(label, handler);
} catch (err) {
if (err.message.includes('already defined')) return; // idempotent setup
throw err;
} Prevention
- Guard router setup behind a module-level singleton/flag
- Make route registration idempotent (check before add)
- Avoid registering routes in loops or hot-reloaded modules
- Keep one canonical place where all routes are registered
When it happens
Trigger: Calling router.addHandler('products', h) twice, or addDefaultHandler(h) after a default handler was already registered.
Common situations: Module executed twice (double import, hot reload) registering routes again; initializing the router in a loop; two code paths both adding a default handler; copy-pasted registration blocks.
Related errors
- Route not found for label '${String(label)}'. You must set u
- The `minConcurrency`/`maxConcurrency`/`initialConcurrency`/`
- The `requestManager` option cannot be used in conjunction wi
- Cannot decide what to purge before running again: `sameDomai
- Failed to infer format from the path: '${path}'. Supported f
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/431822579b40f768.
Report an issue: GitHub.