Skip to content

Diagnostics

Arc reports a problem at the earliest point it can see it. A binding mistake shows in the editor, a missing service stops build(), a bad request answers 400 with a reason, and a running server can describe what it serves. This page lists each of those surfaces and what it tells you. For step-by-step fixes of common problems, see Troubleshooting.

@cratis/eslint-plugin-arc-core checks model-bound artifacts before you build. Enabled rules report as errors. Both presets include bounded Chronicle checks for reactor and command appends and unmasked secret-looking fields when Chronicle is installed; recommended-type-checked also includes reactor replay decisions and the Guid response check. query-argument-name is opt-in. Rules that match a .NET analyzer keep its ARC or ARCCHR code. Setup is in Code analysis.

RuleReports
arc0002A command-like class without @command()
arc0003Command handling outside the command
arc0004A command without a public instance handle()
arc0005A value from provide() that handle() never consumes
arc0010A synchronous command result wrapped in a promise
arc0012A built-in error thrown from an artifact
arc0014A query with type parameters
arc0015An incoming parameter converted to a concept inside the query
arc0019@allowAnonymous() combined with @authorize() or @roles()
arcchr0003Direct reactor appends to its own default event log
arcchr0006Returned Arc commands from live reactor handlers without a replay decision (type-checked preset)
arcchr0007Direct default-log appends from a command’s handle() or provide(), including injected Chronicle services and .transactional appends
arcchr0009Unmasked secret-looking command fields and constructor parameter properties when Chronicle resolves
arcchr0010A keyless command returning a type-checked Fundamentals Guid value beside a direct decorated event (type-checked preset)
missing-fieldA model-bound property without @field
declared-fieldA decorated field that would not be emitted
inject-binding@inject tokens that do not match the handler’s parameters
query-binding@query descriptors that do not match the method’s parameters
query-argument-nameA query wire name that differs from its parameter name (opt-in)
misplaced-decoratorAn Arc decorator on an artifact that does not support it
unexported-artifactA decorated artifact that discovery cannot see because it is not exported
validator-targetA validator without @validator(Target) when no generated metadata names it

Chronicle code analysis maps the remaining .NET diagnostics to TypeScript checks or review concerns.

  • The CLI exits with code 1 and prints the error when generation fails, such as for a type it cannot map or a dynamic decorator option. See Type mapping.
  • A validator rule that needs server evaluation, such as must or when, stays on the server, and the generator prints a diagnostic naming it. See Validation rules.
  • --check-metadata compares the generated metadata file with the source and fails when they differ, without generating anything; it prints Generated artifact metadata is current when they match. See Generated artifact metadata.

builder.add(...), the with... integrations, and builder.build() refuse an application that cannot run correctly, instead of failing on the first request. Typical messages:

MessageMeaning
Not an Arc artifact: <Type>A class passed to add() has no Arc or integration decorator
Conflicting namespaces for <Type>One class was registered under two different namespaces
Duplicate validator target: <Type>Two validators target one class
Unbound handle parameters on <Type>.handle; use builder.useGeneratedMetadata(metadata) or @inject(...); default and rest parameters require explicit bindingA command’s handle() has parameters without generated metadata or @inject(...) tokens; see Troubleshooting
Unbound provide parameters on <Type>.provide; use builder.useGeneratedMetadata(metadata) or @inject(...); default and rest parameters require explicit bindingThe same for a command’s provide()
Unbound parameters on <Type>.<method>A query’s @query(...) descriptors do not cover every parameter of the method
Missing parameter metadata for <Type>.<method>; use explicit tokensA bare @query() or an empty @inject() on a method with parameters, without generated metadata, in standard decorator mode
Unbound constructor parameters on <Type>A service’s constructor parameters have no tokens
Service <Token> requires an implementationA serviceToken was registered without a class or factory
Missing service: <Token>A declared dependency is not registered
Service dependency cycle: <Token>Services depend on each other in a loop
Captive service dependency: <Token>A singleton depends on a scoped or transient service
Expected one read-model resolver for <Type>, found <n>A commandReadModel(Type) has no owning integration, or two; see When read model resolution fails
Multiple identity details providers foundMore than one @identityDetailsProvider()
Import @cratis/arc.<name> before calling with<Name>()An integration method was called without importing its package
Chronicle requires eventStore and exactly one of connectionString or clientIncomplete withChronicle options or configuration
MongoDB requires exactly one of client, server, or serverResolverIncomplete withMongoDB options or configuration
Drizzle requires exactly one of database or databaseFactoryIncomplete withDrizzle options
Invalid Cratis configuration: <path>.<problem>An appsettings.json or Cratis__... value has the wrong type; the message names the key, never the value

The service messages are explained in Dependency injection.

A command or query result carries validationResults, and each result has a reason:

reasonMeaning
ruleA validator rule failed, or a required command read model is missing or has no key
malformedRequestThe input does not match the declared fields
validatorFailedA validator threw; the error goes to logger, and the caller sees a generic message without the exception text
dependencyUnavailableA service a validator or handler needs could not be resolved
constraintViolationChronicle rejected an append for a constraint; reasonDetail names it
concurrencyViolationChronicle rejected an append because the stream moved; state holds the revisions

The HTTP status follows the HTTP contract reference: 400 for validation, 401 and 403 for authentication and authorization, 405 for an unsupported method, 408 and 503 for observable waits and limits, and 500 for exceptions. Outside development, a 500 carries An unexpected error occurred and no stack trace; the original error goes to the logger option. See Configuration.

SurfaceShows
GET /.cratis/commands, GET /.cratis/queriesEvery command and query with its route and input JSON Schema; see Introspection
GET /.cratis/identity-details/schemaThe identity details schema
GET /openapi.jsonThe OpenAPI 3.1 document; see OpenAPI
GET /.cratis/queries/healthThe authenticated caller’s own observable hub connections, when query.enableObservableHealth is on; see Query health
OpenTelemetrySpans such as cratis.arc.command.execute and the cratis.arc.command.duration / cratis.arc.query.duration histograms in seconds from the versioned Cratis.Arc scope; cratis.arc.operation.duration remains emitted but is deprecated; see Observability
logger(error, correlationId)Every failure with its correlation ID, and unknown configuration keys

Every response carries its correlation ID, in X-Correlation-ID unless you renamed the header. Search your logs and traces by it.

For the Chronicle side of a running system, such as failed observer partitions, use the Chronicle Workbench or the cratis CLI against the same event store and tenant namespace.