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.
In the editor: lint rules
Section titled “In the editor: lint rules”@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.
| Rule | Reports |
|---|---|
arc0002 | A command-like class without @command() |
arc0003 | Command handling outside the command |
arc0004 | A command without a public instance handle() |
arc0005 | A value from provide() that handle() never consumes |
arc0010 | A synchronous command result wrapped in a promise |
arc0012 | A built-in error thrown from an artifact |
arc0014 | A query with type parameters |
arc0015 | An incoming parameter converted to a concept inside the query |
arc0019 | @allowAnonymous() combined with @authorize() or @roles() |
arcchr0003 | Direct reactor appends to its own default event log |
arcchr0006 | Returned Arc commands from live reactor handlers without a replay decision (type-checked preset) |
arcchr0007 | Direct default-log appends from a command’s handle() or provide(), including injected Chronicle services and .transactional appends |
arcchr0009 | Unmasked secret-looking command fields and constructor parameter properties when Chronicle resolves |
arcchr0010 | A keyless command returning a type-checked Fundamentals Guid value beside a direct decorated event (type-checked preset) |
missing-field | A model-bound property without @field |
declared-field | A 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-name | A query wire name that differs from its parameter name (opt-in) |
misplaced-decorator | An Arc decorator on an artifact that does not support it |
unexported-artifact | A decorated artifact that discovery cannot see because it is not exported |
validator-target | A validator without @validator(Target) when no generated metadata names it |
Chronicle code analysis maps the remaining .NET diagnostics to TypeScript checks or review concerns.
At generation: arc-proxygenerator
Section titled “At generation: arc-proxygenerator”- 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
mustorwhen, stays on the server, and the generator prints a diagnostic naming it. See Validation rules. --check-metadatacompares the generated metadata file with the source and fails when they differ, without generating anything; it printsGenerated artifact metadata is currentwhen they match. See Generated artifact metadata.
At startup: build() and registration
Section titled “At startup: build() and registration”builder.add(...), the with... integrations, and builder.build() refuse an application that cannot run correctly, instead of failing on the first request. Typical messages:
| Message | Meaning |
|---|---|
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 binding | A 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 binding | The 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 tokens | A 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 implementation | A 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 found | More 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 client | Incomplete withChronicle options or configuration |
MongoDB requires exactly one of client, server, or serverResolver | Incomplete withMongoDB options or configuration |
Drizzle requires exactly one of database or databaseFactory | Incomplete 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.
At request time: results and status codes
Section titled “At request time: results and status codes”A command or query result carries validationResults, and each result has a reason:
reason | Meaning |
|---|---|
rule | A validator rule failed, or a required command read model is missing or has no key |
malformedRequest | The input does not match the declared fields |
validatorFailed | A validator threw; the error goes to logger, and the caller sees a generic message without the exception text |
dependencyUnavailable | A service a validator or handler needs could not be resolved |
constraintViolation | Chronicle rejected an append for a constraint; reasonDetail names it |
concurrencyViolation | Chronicle 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.
On a running server
Section titled “On a running server”| Surface | Shows |
|---|---|
GET /.cratis/commands, GET /.cratis/queries | Every command and query with its route and input JSON Schema; see Introspection |
GET /.cratis/identity-details/schema | The identity details schema |
GET /openapi.json | The OpenAPI 3.1 document; see OpenAPI |
GET /.cratis/queries/health | The authenticated caller’s own observable hub connections, when query.enableObservableHealth is on; see Query health |
| OpenTelemetry | Spans 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.