Troubleshooting
Each entry names the symptom, the usual cause, and the fix, with a link to the page that has the full contract.
Installation
Section titled “Installation”Installing Arc outside the clone fails
Section titled “Installing Arc outside the clone fails”npm install @cratis/arc.core fails with 404 Not Found, or installing a tarball fails with Unsupported URL Type "workspace:": workspace:^. The Arc packages are not published to npm, and a workspace:^ dependency resolves only inside this repository’s Yarn workspace. Build a clone, pack the packages you need with yarn workspace <package> pack, and install the tarballs. Do not use npm pack: it leaves the internal workspace:^ dependencies in the packed manifest, and the tarball fails with the second error. See Create an application.
Corepack is missing
Section titled “Corepack is missing”corepack enable fails with command not found. Node.js 25 and later no longer include Corepack. Install it with npm install --global corepack, then run corepack enable again. The repository pins its Yarn version in package.json, and Corepack selects that version.
Decorators and compilers
Section titled “Decorators and compilers”TS1241: Unable to resolve signature of method decorator
Section titled “TS1241: Unable to resolve signature of method decorator”In standard decorator mode, @inject(...) and @query(...) type-check the method’s parameters against the tokens and descriptors you list. The error almost always means they disagree: a missing or extra token, the wrong order, or a handle() that forgot the provide() result as its first parameter. Make the list match the signature one to one. See Dependency injection.
“SyntaxError: Invalid or unexpected token” at a decorator when running .ts directly
Section titled ““SyntaxError: Invalid or unexpected token” at a decorator when running .ts directly”Node.js type stripping removes types but does not transform decorators, so node file.ts fails at the first @. Compile first with tsc (the sample’s yarn build) or with esbuild, which lowers standard decorators for target: "es2022", and run the emitted JavaScript. During development, run the source with tsx watch, as Create an application does.
Decorators do nothing, or metadata is missing, in Vitest or Vite
Section titled “Decorators do nothing, or metadata is missing, in Vitest or Vite”The test transformer must lower standard decorators with useDefineForClassFields: true. This repository adds an esbuild pre-transform plugin for .ts files in its vite.base.ts, with target: 'es2022' and experimentalDecorators: false. Use the same approach in your own Vitest configuration.
build() fails with “Unbound handle parameters” or “Missing parameter metadata”
Section titled “build() fails with “Unbound handle parameters” or “Missing parameter metadata””This is the usual first-run failure of a new application. TypeScript erases parameter types at runtime, so without help Arc cannot tell that handle(tasks: Tasks) needs the Tasks service. build() stops with one of these messages:
| Message | Cause |
|---|---|
Unbound handle parameters on <Type>.handle; use builder.useGeneratedMetadata(metadata) or @inject(...); default and rest parameters require explicit binding | A command’s handle() has parameters and no tokens |
Unbound provide parameters on <Type>.provide; use builder.useGeneratedMetadata(metadata) or @inject(...); default and rest parameters require explicit binding | The same for provide() |
Missing parameter metadata for <Type>.<method>; use explicit tokens | A bare @query() or an empty @inject() on a method with parameters, in standard decorator mode |
Unbound parameters on <Type>.<method> | The @query(...) descriptors do not cover every parameter of the method |
Fix it one of two ways:
- Generate artifact metadata with
arc-proxygenerator --metadata <file>and callbuilder.useGeneratedMetadata(metadata)beforediscover()oradd(), as the Tasks sample does. See Generate artifact metadata. - List the tokens yourself:
@inject(Tasks)onhandle(), and@query(argument('id', TaskId), service(Tasks))in parameter order on a query.
Default and rest parameters always need explicit tokens. In legacy experimentalDecorators mode with emitDecoratorMetadata, an empty @inject() or a bare @query() infers class-valued parameters from design:paramtypes; a parameter it cannot infer fails with Unresolvable parameter on <Type>.<method>; use explicit tokens. See Dependency injection.
The server stops with stale generated artifact metadata
Section titled “The server stops with stale generated artifact metadata”builder.useGeneratedMetadata(metadata) throws Stale generated artifact metadata for <Type>; regenerate artifact metadata, or arc-proxygenerator --check-metadata fails with Stale or missing generated artifact metadata: <file>; regenerate artifact metadata. An artifact changed after the metadata module was generated, for example a field or the number of handle() parameters. A query method added since the last run fails as Missing parameter metadata for <Type>.<method> instead, described above.
Run the generator again with the same options. While you develop, keep it running with --watch beside your tsx process; tsx may restart once with the old module before the generator finishes. Reordering parameters without changing their count goes undetected, so regenerate after every artifact edit and gate CI with --check-metadata. See Generate artifact metadata.
Module resolution
Section titled “Module resolution”NodeNext or Bundler?
Section titled “NodeNext or Bundler?”Server code that imports @cratis/arc.core and @cratis/fundamentals 7.22.0 type-checks with module and moduleResolution set to NodeNext, or with ESNext and Bundler as the Tasks sample does. With NodeNext and native ESM, keep .js extensions on relative imports.
Generated frontend proxies are different: the published @cratis/arc client declarations use extensionless imports, so compile proxies in Bundler mode. NodeNext consumer compilation of generated proxies is not supported. See Proxy generation.
Discovery
Section titled “Discovery”discover() refuses the folder
Section titled “discover() refuses the folder”discover() refuses a folder that contains the entry point or an imported bootstrap that is itself calling discovery, because importing it would re-enter a suspended top-level await. Put artifacts in a dedicated folder such as Features/. It also rejects a folder that mixes emitted .js and loader-backed .ts files.
An artifact is not found
Section titled “An artifact is not found”discover() only registers exported classes, and skips dist, node_modules, given, for_*, index.*, declaration files, and symbolic links. A validator that is never imported is never registered: add it with builder.add(...) or keep it under the discovery root.
Routes changed after a refactor, or are wrong in a bundled build
Section titled “Routes changed after a refactor, or are wrong in a bundled build”Routes derive from folders and class names. A folder move changes the route, and a bundler that renames classes changes it too. Pin routes with @command({ namespace }), @readModel({ namespace }), or @path(...), and keep class names (keepNames in esbuild). See Endpoint mapping.
“Not an Arc artifact”
Section titled ““Not an Arc artifact””builder.add(...) received a class without an Arc decorator. Check that the class has @command(), @readModel(), @validator(...), a lifetime decorator, or one of the extension-point decorators.
Hosting
Section titled “Hosting”Every command answers 400 malformedRequest behind Express
Section titled “Every command answers 400 malformedRequest behind Express”A body parser such as express.json() ran before Arc and consumed the body. Call app.use(cratisArc(arc)) before adding body parsers. See Express.
Fastify answers 413 for a large body
Section titled “Fastify answers 413 for a large body”Fastify’s bodyLimit, 1 MiB by default, applies before Arc’s hosting.maxBodyBytes. Raise both. See Fastify.
A browser WebSocket never opens from a dev server
Section titled “A browser WebSocket never opens from a dev server”The page’s origin differs from the server’s, so the Origin check refuses the upgrade. Add the dev server origin, and your application origin, to query.allowedOrigins. See WebSockets.
An observable query answers 202
Section titled “An observable query answers 202”The source has no current value yet. Use RxJS new BehaviorSubject(value) for a current value, or ask with waitForFirstResult=true. An emission guard that suppresses the snapshot also answers 202. See Using observable queries with curl.
Security
Section titled “Security”A protected operation answers 403 where you expected 401
Section titled “A protected operation answers 403 where you expected 401”No authentication handler is configured, so nobody is authenticated and the declaration fails at the authorization step. Configure authentication, or a native principal.
The server refuses to start with native principal and handlers
Section titled “The server refuses to start with native principal and handlers”nativePrincipal: true and authentication handlers are mutually exclusive. Choose one boundary. See Authentication.
Generated clients
Section titled “Generated clients”The generated client calls a route that answers 404
Section titled “The generated client calls a route that answers 404”The generator’s route options do not match the server’s generatedApis settings or discovery root namespace. Align --api-prefix, --segments-to-skip, --root-namespace, and the name-in-route switches. See Proxy generator configuration.
“Ambiguous model name”
Section titled ““Ambiguous model name””The proxy generator fails with Ambiguous model name: <Namespace>.<Name> when two different model declarations have the same namespace and class name. The generator derives a model’s namespace from its folder below the artifacts root, so this happens when two files in one folder declare the same model name. Models with the same class name in different folders are supported. Rename one of the two, or move it to another folder.
Chronicle
Section titled “Chronicle”Commands or queries fail after registering Chronicle late
Section titled “Commands or queries fail after registering Chronicle late”withChronicle can run before or after discover(): Arc replays discovered artifact types to Chronicle when you register it. If routes still fail, check that the relevant event types and read models are exported beneath the discovery root. When using add() for Chronicle-only artifacts, call withChronicle first so add() recognizes them. See Add event sourcing.
A command or query that uses Chronicle never answers
Section titled “A command or query that uses Chronicle never answers”The Chronicle kernel is not running, or not reachable at the connection string’s address. The server starts without it, and the SDK keeps trying to connect, so the request waits instead of failing. It completes once the kernel is up. Check the kernel’s health; see Start a development kernel.
Chronicle commands and queries answer 500 with “An unexpected error occurred”
Section titled “Chronicle commands and queries answer 500 with “An unexpected error occurred””Check that event types and projections are exported beneath the discovery root. If add(...) registers Chronicle-only artifacts, call withChronicle first so Arc recognizes them. See Registration options.
Every command waits for the full completion timeout, then answers 500
Section titled “Every command waits for the full completion timeout, then answers 500”The application sets completionTimeoutMs, and it has a projection or reactor that does not handle the appended event type. Chronicle waits for every observer on the event log, so the wait times out although the events were appended. This is Cratis/Chronicle#4132. Remove completionTimeoutMs, or see Make the command wait. A kernel test scenario behaves the same way; see Register only the observers the scenario needs.
Do I need to import reflect-metadata?
Section titled “Do I need to import reflect-metadata?”Not for the Chronicle SDK 6.7. It depends on reflect-metadata, and each of its modules that reads decorator metadata imports it. Importing it first in your entry point is harmless. A browser frontend that uses the Arc client packages still imports it; see Set up proxy generation.
Related
Section titled “Related”- Capability reference for what is and is not implemented
- Code analysis to catch many of these in the editor