Error codes
Framework errors thrown by core, every warlock doctor finding and every warlock check problem carry a stable code such as WL3001. Look the code up instead of interpreting the message:
warlock explain WL3001 # what it means, the fix, the skill and this page's anchorwarlock explain WL3001 --json # the same as JSONwarlock explain # list every codeWL3001 A route name is already taken
Area: routing (your code) Fix: Rename one of the two routes with the "name" route option; pages and API routes share one namespace. Skill: register-route Docs: https://warlock.js.org/v/latest/core/cli/error-codes/#wl3001 Thrown as: RouteNameTakenErrorThe scheme
Section titled “The scheme”A code is WL followed by four digits. The first digit is the area:
| Range | Area | Who acts |
|---|---|---|
| WL1xxx | config | your code: a configuration value is missing or invalid |
| WL2xxx | boot | your code: a module, connector or service could not boot |
| WL3xxx | routing | your code: route registration and lookup |
| WL4xxx | validation | your code: request validation schemas |
| WL5xxx | http | your code: the HTTP layer at runtime |
| WL6xxx | database | your code: migrations, seeders, database code |
| WL7xxx | cli | your code: CLI usage, and the type/lint/test problems check finds |
| WL8xxx | environment | this machine: Node version, tools, services, ports |
| WL9xxx | convention | your code: framework conventions and release hygiene |
Every entry also has a category — code or environment — and only the WL8xxx codes are environment. That is the split warlock check turns into its exit codes: exit 1 for your code, exit 2 for this machine.
Stability
Section titled “Stability”- A released code keeps its meaning forever. It is never renumbered and never reused for a different problem.
- The title, fix and links may be reworded; match on the code, not the text.
- A code that is no longer emitted stays in the table, marked retired.
- New codes are added in the area’s range.
Reading a code in code
Section titled “Reading a code in code”The registry is on @warlock.js/core/errors (side-effect free) and on the root @warlock.js/core:
import { ERROR_CODES, errorCodeOf, explainErrorCode, WarlockError } from "@warlock.js/core/errors";
try { await startServer();} catch (error) { const code = errorCodeOf(error); // "WL8004" for a busy port, "WL8003" for ECONNREFUSED, …
if (code) { console.error(`${code}: ${explainErrorCode(code)?.fix}`); }
throw error;}
throw new WarlockError("WL3003", `Route name "${name}" not found`);- Framework error classes carry the code as a
codeproperty (new ModuleLoadError(...).code === "WL2001"). PortInUseError.codestays Node’s"EADDRINUSE";errorCodeOf()maps it toWL8004by class name, so useerrorCodeOf()rather than reading.codedirectly. (MailError.codekeeps its own mail codes.)errorCodeOf()also maps Node system errors (ECONNREFUSED,ENOTFOUND,EHOSTUNREACH,ETIMEDOUT→WL8003;EADDRINUSE→WL8004) and followserror.cause.- HTTP errors you throw from handlers (
BadRequestError,ResourceNotFoundError, …) are responses, not framework failures, and carry no code.
Every code follows, grouped by area.
WL1xxx — config
Section titled “WL1xxx — config”WL1001
Section titled “WL1001”A required config section is missing. Add the missing file under src/config/ (for example src/config/app.ts) exporting the section.
Category: your code · skill: configure-app
WL1002
Section titled “WL1002”No JWT signing secret is configured. Run warlock jwt.generate (writes JWT_SECRET to .env) and read it as auth.accessToken.secret in src/config/auth.ts.
Category: your code · skill: configure-app
WL1003
Section titled “WL1003”The rate-limit store cannot use the configured cache driver. Use a redis, pg or memory cache driver for http.rateLimit store “cache”, or set store: “memory”.
Category: your code · skill: configure-app · thrown as UnsupportedRateLimitDriverError
WL1004
Section titled “WL1004”An http.csp directive value is invalid. Fix the directive value named in the message in your http config; quotes must balance and values may not contain ’;’.
Category: your code · skill: configure-app · thrown as InvalidCspDirectiveError
WL1005
Section titled “WL1005”The uploads.images configuration is invalid. Correct the uploads.images variant settings named in the message.
Category: your code · skill: upload-file · thrown as ImageVariantsConfigError
WL1006
Section titled “WL1006”warlock.config.ts sets a build option the builder refuses. Remove the named option from the build block of warlock.config.ts; change tsconfig.json instead.
Category: your code · skill: run-app · thrown as ForbiddenBuildOptionError
WL1007
Section titled “WL1007”A locale dictionary file cannot be parsed. Fix the key named in the message in the locales.json file it names.
Category: your code · skill: use-localization · thrown as LocaleDictionaryError
WL1008
Section titled “WL1008”A locale outside app.localeCodes was requested. Add the locale to app.localeCodes in src/config/app.ts, or pass a configured locale.
Category: your code · skill: use-localization · thrown as UnknownLocaleError
WL1009
Section titled “WL1009”warlock.config.ts > connectors was refused. Give every connector in warlock.config.ts a unique name that is not a built-in connector name.
Category: your code · skill: add-connector
WL1010
Section titled “WL1010”A locale dictionary has translations for a locale the app does not configure. Add the locale to app.localeCodes in src/config/app.ts, or delete that locale’s text from the locales.json file named in the warning. The text is ignored until then.
Category: your code · skill: use-localization
WL2xxx — boot
Section titled “WL2xxx — boot”WL2001
Section titled “WL2001”An app module failed to load. Open the file named in the message and fix the error it throws on import; its routes and listeners are missing until then.
Category: your code · skill: warlock-doctor · thrown as ModuleLoadError
WL2002
Section titled “WL2002”The app’s source files could not be discovered. Run the command from the project root and check that src/ exists and is readable.
Category: your code · skill: warlock-doctor
WL2003
Section titled “WL2003”A connector failed to boot. Read the connector’s message; it usually names the config value or package it needs.
Category: your code · skill: add-connector
WL2004
Section titled “WL2004”A container key was read before it was registered. Register the key (container.set) before reading it, or read it after the connector that sets it has booted.
Category: your code · skill: use-app-context · thrown as ContainerKeyMissingError
WL2005
Section titled “WL2005”A boot precondition failed. Fix the condition named in the message; restarting does not help until it changes.
Category: your code · skill: run-app · thrown as BootPreconditionError
WL2006
Section titled “WL2006”Storage was used before the storage connector ran. Add src/config/storage.ts; in a custom command add “storage” to preload.connectors.
Category: your code · skill: store-file · thrown as StorageNotInitializedError
WL2007
Section titled “WL2007”A configured driver’s package is not installed. Install the package named in the message (for example pnpm add sharp), or stop configuring the driver.
Category: your code · skill: update-packages
WL2008
Section titled “WL2008”A capability was used, or required at boot, without a provider. Call provideCapability(capability, implementation) from a module that loads at boot (main.ts, routes.ts or an event file), or define the capability with { required: false } if it is optional.
Category: your code · skill: use-capabilities · thrown as CapabilityNotProvidedError
WL3xxx — routing
Section titled “WL3xxx — routing”WL3001
Section titled “WL3001”A route name is already taken. Rename one of the two routes with the “name” route option; pages and API routes share one namespace.
Category: your code · skill: register-route · thrown as RouteNameTakenError
WL3002
Section titled “WL3002”A route points at a controller method that does not exist. Fix the [controller, action] pair: the action must be a method on the controller.
Category: your code · skill: register-route
WL3003
Section titled “WL3003”A URL was built for a route name that is not registered. Check the name passed to router.route(); warlock routes --name <part> lists the registered names.
Category: your code · skill: warlock-routes
WL3004
Section titled “WL3004”A URL was built without one of the route’s path parameters. Pass every :param of the route path when building the URL.
Category: your code · skill: warlock-routes
WL3005
Section titled “WL3005”Route modules loaded but registered no routes. Make sure routes.ts calls router.get/post/… at module level and is not wrapped in an unused function.
Category: your code · skill: register-route
WL3006
Section titled “WL3006”A handler uses the v4 (request, response) signature. Rewrite the handler as async ({ request, response }) => { … }.
Category: your code · skill: create-controller
WL4xxx — validation
Section titled “WL4xxx — validation”WL4001
Section titled “WL4001”A validation schema cannot be described as JSON Schema. Use Seal validators with a static shape for validation.schema/params; custom validate middleware is not described.
Category: your code · skill: validate-input
WL4002
Section titled “WL4002”A request-schema field has no translated label in a configured locale. Add the field under attributes (or the configured validation.attributeGroup) in each locale file, or turn the check off with validation.warnMissingLabels: false.
Category: your code · skill: validate-input
WL5xxx — http
Section titled “WL5xxx — http”WL5001
Section titled “WL5001”A cookie was read but the cookie plugin is not registered. Register @fastify/cookie on the server before reading cookies by name.
Category: your code · skill: send-response · thrown as CookieJarUnavailableError
WL5002
Section titled “WL5002”An app route claims a health-probe path. Move the app route, or rename the probe with http.health.path / http.health.readinessPath.
Category: your code · skill: health-checks
WL5003
Section titled “WL5003”Health probes are enabled but not registered. Check that the http connector booted and that nothing removed the probe routes.
Category: your code · skill: health-checks
WL5004
Section titled “WL5004”Devtools recorded no timeline for the traced request. Keep http.requestId enabled (its echoed header is how the request is found) and retry; a path under the devtools prefix is never recorded.
Category: your code · skill: warlock-trace
WL5005
Section titled “WL5005”A site’s root page answered 5xx in the doctor render. Read the phase and page file doctor prints, fix that stage, then run warlock doctor again.
Category: your code · skill: warlock-doctor
WL6xxx — database
Section titled “WL6xxx — database”WL6001
Section titled “WL6001”A seeder depends on a seeder that is not registered. Fix the dependsOn name, or register the missing seeder.
Category: your code · skill: write-seeder · thrown as UnknownSeederDependencyError
WL6002
Section titled “WL6002”Seeder dependencies form a cycle. Remove one dependsOn edge from the cycle printed in the message.
Category: your code · skill: write-seeder · thrown as SeederDependencyCycleError
WL7xxx — CLI and checked code
Section titled “WL7xxx — CLI and checked code”WL7001
Section titled “WL7001”A boolean CLI option received a value it cannot read. Pass true/false/1/0/yes/no, or the flag on its own for true.
Category: your code · skill: write-cli-command · thrown as CliOptionValueError
WL7002
Section titled “WL7002”Generated code imports a package the app does not declare. Add the named package to the app’s dependencies, or import it through @warlock.js/core.
Category: your code · skill: run-app · thrown as UndeclaredGeneratedImportError
WL7003
Section titled “WL7003”A command needs an optional package that is not installed. Install the packages named in the message as dev dependencies (warlock mcp needs @warlock.js/ai-tools and @warlock.js/ai).
Category: your code · skill: warlock-mcp
WL7004
Section titled “WL7004”warlock mcp was asked to listen off loopback without a token. Pass —token (or set WARLOCK_MCP_TOKEN) when —host is not 127.0.0.1, ::1 or localhost, or keep the default loopback host.
Category: your code · skill: warlock-mcp
WL7005
Section titled “WL7005”An MCP tool that writes files was called on a read-only server. Restart the server with —allow-writes to let generate tools write; without it they only return the diff.
Category: your code · skill: warlock-mcp
WL7101
Section titled “WL7101”TypeScript reports a type error. Fix the type error at the reported position; the TypeScript code (TS1234) is in rule.
Category: your code · skill: warlock-check
WL7102
Section titled “WL7102”tsconfig.json is missing or invalid. Fix tsconfig.json at the project root; the message names the problem.
Category: your code · skill: warlock-check
WL7103
Section titled “WL7103”Generated typings may be stale. Run warlock generate.typings, then typecheck again; the type errors it reports may disappear.
Category: your code · skill: warlock-check
WL7201
Section titled “WL7201”The linter reports an error. Fix the lint error at the reported position; the rule id is in rule.
Category: your code · skill: warlock-check
WL7202
Section titled “WL7202”The linter reports a warning. Fix the warning, or change the rule’s severity in the lint config if it does not apply.
Category: your code · skill: warlock-check
WL7301
Section titled “WL7301”A test failed. Run the test file alone, read the assertion message and fix the code or the test.
Category: your code · skill: warlock-check
WL7302
Section titled “WL7302”A test file failed before its tests ran. Fix the import or setup error named in the message; none of the file’s tests ran.
Category: your code · skill: warlock-check
WL7401
Section titled “WL7401”The agent board is not initialized. Run warlock board init at the project root first.
Category: your code · skill: warlock-board
WL7402
Section titled “WL7402”A board item does not exist. Run warlock board status to list the item ids.
Category: your code · skill: warlock-board
WL7403
Section titled “WL7403”A board item cannot move to the requested state. Use one of the next states listed in the message; warlock board status <id> shows the item’s current state.
Category: your code · skill: warlock-board
WL7404
Section titled “WL7404”No board message arrived before the watch timed out. Run warlock board watch again, or raise —timeout.
Category: your code · skill: warlock-board
WL7501
Section titled “WL7501”AGENTS.md has malformed generated-section markers. Keep one <!-- warlock:generated:start --> line followed by one <!-- warlock:generated:end --> line, or remove both.
Category: your code · skill: warlock-agents-md
WL8xxx — this machine
Section titled “WL8xxx — this machine”WL8001
Section titled “WL8001”The Node.js version is not supported. Switch to a Node.js version that satisfies the engines range in the message (for example with nvm).
Category: this machine · skill: update-packages
WL8002
Section titled “WL8002”A tool the project is configured to use is not installed. Install the tool named in the message (pnpm install), or remove its config file.
Category: this machine · skill: warlock-check
WL8003
Section titled “WL8003”A service the code needs is unreachable. Start the service named in the message (database, Redis, …) or point the config at a running one.
Category: this machine · skill: warlock-check
WL8004
Section titled “WL8004”A port is already in use. Stop whatever listens on the port, or use another port (port 0 in tests).
Category: this machine · skill: run-app · thrown as PortInUseError
WL8005
Section titled “WL8005”esbuild’s native binary is not installed. Run pnpm approve-builds, then reinstall dependencies without skipping install scripts.
Category: this machine · skill: run-app · thrown as EsbuildBinaryMissingError
WL8006
Section titled “WL8006”Git cannot list the changed files. Run inside a git repository with git on PATH, or run the command without —changed.
Category: this machine · skill: warlock-check
WL8007
Section titled “WL8007”A tool crashed, timed out or ran out of memory. Re-run the step; if it repeats, raise the memory limit (NODE_OPTIONS=—max-old-space-size) or use —changed.
Category: this machine · skill: warlock-check
WL8008
Section titled “WL8008”The dev server is not running. Start warlock dev in this project, or pass —url with the address of a running dev server.
Category: this machine · skill: warlock-trace
WL8009
Section titled “WL8009”Devtools is not running in the dev server. Install it with warlock add devtools and restart warlock dev; devtools answers loopback requests in development only.
Category: this machine · skill: warlock-trace
WL8010
Section titled “WL8010”Installed @warlock.js packages are on different versions. Run warlock update (or install every @warlock.js/* package at the same version) so the whole family shares one version.
Category: this machine · skill: update-packages
WL9xxx — conventions and hygiene
Section titled “WL9xxx — conventions and hygiene”WL9001
Section titled “WL9001”An auto-loaded file is imported. Remove the import: routes.ts, main.ts, worker.ts and events/ files are loaded by the framework.
Category: your code · skill: warlock-conventions
WL9002
Section titled “WL9002”An import path ends in .js. Drop the .js extension from the import path.
Category: your code · skill: warlock-conventions
WL9003
Section titled “WL9003”A relative import reaches into another module. Import across modules through the app/
Category: your code · skill: warlock-conventions
WL9004
Section titled “WL9004”tsconfig.json enables legacy experimentalDecorators. Remove “experimentalDecorators”: true from tsconfig.json; Cascade uses standard decorators.
Category: your code · skill: lower-stage3-decorators
WL9005
Section titled “WL9005”request.all() is passed to a query, a service or inApp(). Give the route a validation schema and pass request.validated(), which holds only the fields the schema accepted (mass assignment otherwise).
Category: your code · skill: warlock-conventions
WL9006
Section titled “WL9006”A mutating route has no auth guard and is not marked public. Register the POST/PUT/PATCH/DELETE route inside guarded() (or with auth middleware), or pass { public: true } if it is meant to be open. A route inside a helper of your own that wraps guarded() is reported unless you list the helper’s name in guardWrappers in .warlock-check.json. Limits: a guard is recognised by a name containing auth or guard, or as a constant initialised with markAuthGuard() in the same file (an imported guard with another name is not recognised: rename it or add a warlock-check-ignore comment). A route whose options object is only a spread ({ ...opts }) is reported as a warning, because the check cannot see the guard; write middleware or public: true in the literal.
Category: your code · skill: warlock-conventions
WL9007
Section titled “WL9007”A redirect or link target comes from request input. Wrap the target in safeRedirect() (server, @warlock.js/core) or safeRedirectTarget() (pages, @warlock.js/web), or check it with isSafeRedirect() first.
Category: your code · skill: warlock-conventions
WL9008
Section titled “WL9008”A raw email, credential or request body is logged. Pass the value through maskEmail(), redact(), escapeLogValue() or request.toLogSnapshot(), or log it under a key the logger censors (token, password, …).
Category: your code · skill: warlock-conventions
WL9009
Section titled “WL9009”A regex with an unbounded quantifier runs on request input. Bound the repetition ({1,256} instead of + or *) or cap the input length before matching.
Category: your code · skill: warlock-conventions
WL9010
Section titled “WL9010”await on a synchronous log call. Remove the await: log.info() and its siblings return the logger; await log.flush() waits for delivery.
Category: your code (a warning: it does not fail warlock check) · skill: warlock-conventions
WL9011
Section titled “WL9011”A file defines more than one controller. Move each extra controller into its own <name>.controller.ts file.
Category: your code (a warning: it does not fail warlock check) · skill: warlock-conventions
WL9012
Section titled “WL9012”A tagged union uses kind as its discriminator. Rename the discriminator to type and update the code that narrows on it.
Category: your code (a warning: it does not fail warlock check) · skill: warlock-conventions
WL9101
Section titled “WL9101”A module still uses the deprecated utils/locales.ts. Run warlock doctor --fix to convert it to locales.json, or convert it by hand.
Category: your code · skill: use-localization
WL9102
Section titled “WL9102”package.json version and CHANGELOG.md disagree. Add a CHANGELOG.md heading for the package.json version, or fix the version.
Category: your code · skill: warlock-doctor
WL9103
Section titled “WL9103”CHANGELOG.md is missing or has no version heading. Add a CHANGELOG.md with a ## x.y.z heading for the current version.
Category: your code · skill: warlock-doctor
WL9104
Section titled “WL9104”Translation key is computed at runtime, so the page cannot ship only the keys it uses. Use a literal key or a static-prefix template such as statuses.${status} instead of a computed key. A plain config.translations array only adds keys, so one dynamic site anywhere in the page’s graph (page, layouts, root, error pages) still ships every translation group; declare config.translations = { keys: [...], exhaustive: true } in the setup file when the key set is closed, and the declared keys cover the computed lookups for that page. Reported as a warning on src/web files with the file, line and key text; the build log lists the same sites.
Category: your code · skill: use-localization
WL9013
Section titled “WL9013”Component file exceeds the line limit. Split the component file or configure componentLineLimit (default 150). Defaults to a warning; configure severity in .warlock-check.json.
npx warlock explain WL9013WL9014
Section titled “WL9014”More than one component per file. Move extra components to their own TSX files. Defaults to a warning; configure severity in .warlock-check.json.
npx warlock explain WL9014WL9015
Section titled “WL9015”State, effects or handlers inside components. Move state, effects and event-handler functions into a use-*.ts hook. Defaults to a warning; configure severity in .warlock-check.json.
npx warlock explain WL9015WL9016
Section titled “WL9016”Native app form control. Use a design-system control; native controls are permitted inside src/web/design-system/**. Defaults to a warning; configure severity in .warlock-check.json.
npx warlock explain WL9016WL9017
Section titled “WL9017”Native DOM validation. Use shared schema validation and design-system field errors. Defaults to a warning; configure severity in .warlock-check.json.
npx warlock explain WL9017WL9018
Section titled “WL9018”Controller has no response schema. Assign handler.responseSchema to an exported RequestHandler in src/app/**/controllers. Defaults to a warning; configure severity in .warlock-check.json.
npx warlock explain WL9018WL9019
Section titled “WL9019”A browser route call (apiHref() or useSubmitForm({ route })) in src/web takes a route name that is only known at run time, so the page’s route table cannot include it. Pass a string literal (or a template with a static prefix), or declare the names with config.apiRoutes in a page setup file. Defaults to a warning, and no warning is reported once any page declares config.apiRoutes; configure severity in .warlock-check.json.
npx warlock explain WL9019WL9020
Section titled “WL9020”Browser code names a route registered with clientVisible: false. Such a route is never embedded in a page, so the call would fail in the browser. Call the endpoint from the server, or remove clientVisible: false from the route. Defaults to an error; configure severity in .warlock-check.json.
npx warlock explain WL9020WL9021
Section titled “WL9021”A Cascade column is read or written as a property of a model instance (user.email). Model data lives behind get() and set(), so the property is undefined at runtime and a write never reaches the dirty tracker. Use user.get("email") and user.set("email", value). The check recognises instances from new User(), await User.find() and the other single-record finders, a User annotation and this inside a model class, and compares the name with the keys of the model’s static schema when they can be read statically. warlock check --fix rewrites reads and plain model.column = value; statements. Defaults to a warning; configure severity in .warlock-check.json.
npx warlock explain WL9021WL9022
Section titled “WL9022”Server-only code imports a .client module. A *.client.ts or *.client.tsx module is a client-only boundary: pages, layouts and components may import it, but controllers, services, repositories, models, routes, connectors, main.ts, event files and *.server.ts files may not, directly or through another file. The message names the import chain. Pass the data through a loader, or move the shared logic into a file that is neither .client nor server-only. Defaults to an error; configure severity in .warlock-check.json.
npx warlock explain WL9022WL9023
Section titled “WL9023”An app module keeps a hand-maintained index.ts barrel. Import from the file that defines the export and delete the barrel. The rule is off by default; turn it on with { "rules": { "WL9023": "warning" } } in .warlock-check.json.
npx warlock explain WL9023WL9024
Section titled “WL9024”An integration spec (any *.spec.ts or *.test.ts that is not *.unit.*, or any file under tests/) imports no @warlock.js package other than seal, no app/... module, and no file that does. It runs in the integration project and pays for the booted app without using it. Rename it to <name>.unit.spec.ts outside tests/ so the unit project runs it. Defaults to a warning; configure severity in .warlock-check.json, or silence one file with // warlock-check-ignore WL9024 on its first line.
npx warlock explain WL9024WL9025
Section titled “WL9025”model.merge({ column: {...} }) on a Cascade model whose static schema declares column as an object, record or array-of-objects column. merge() deep-merges plain objects, so a key the new value leaves out stays stored (for example a cleared filter comes back after save()). Use model.set({ column: value }) or model.set("column", value) to replace the column. The check only flags receivers it can resolve to a model class (new User(), the single-record finders, a User annotation, this in a model class) and columns whose validator it can read statically (v.object, v.record, v.array(v.object(...))). There is no automatic fix because the two methods behave differently. Defaults to a warning; configure severity in .warlock-check.json.
npx warlock explain WL9025WL9026
Section titled “WL9026”A convention rule of warlock check used more than its time budget over the run (30 seconds by default) and was skipped for the remaining files, so its findings are incomplete. The message names the rule and the time it used. Use --changed to check fewer files, raise ruleBudgetMs in .warlock-check.json (0 turns the budget off), or turn the rule off with rules. Defaults to a warning.
npx warlock explain WL9026WL9027
Section titled “WL9027”A file that the production server bundle compiles imports a static asset (./logo.svg) or a ?raw or ?url query (some-pkg/dist/worker.mjs?worker&url); a bare ?worker query is reported too, with wording that says it may fail. warlock dev accepts these imports, but pages are compiled into the server bundle, which supports plain code imports only, so warlock build fails on them. The files that count are every non-test source file under src/web and every project file they import by value; a file only the browser build reads is not reported. The message names the file, the import and the fix: move the static file into the application’s public/ directory and reference it by its root URL (public/logo.svg becomes /logo.svg). warlock dev prints the same warning, once per file, when the server render evaluates it. Defaults to a warning; configure severity in .warlock-check.json.
npx warlock explain WL9027WL9105
Section titled “WL9105”A translation key in a module’s locales.json or locales/<feature>.json is missing one of the configured locales. The configured locales are localeCodes in src/config/app.ts or in .warlock-check.json; the rule is skipped when neither is set. Defaults to an error; configure severity in .warlock-check.json.
npx warlock explain WL9105