Troubleshooting
Common symptoms when wiring Elarion, their likely cause, and the fix.
Most Elarion problems surface at build time as a missing generated method or an analyzer diagnostic rather than a runtime failure. This table maps the common symptoms to fixes.
Generation and registration
| Symptom | Likely cause | Fix |
|---|---|---|
Add{Module}Handlers is missing | [assembly: UseElarion] / [assembly: GenerateModuleHandlers] absent, or the generator is not referenced as an analyzer. | Add the trigger attribute and the analyzer project/package reference. |
Add{Module}Services is missing | Trigger absent, no [Service] classes found in the module namespace, or the analyzer reference is missing. | Add the trigger, annotate service classes with [Service], verify the analyzer reference. |
| A handler is not registered | The class does not implement Elarion.Abstractions.IHandler<TRequest, TResponse>, or it is outside the module namespace. | Check the interface namespace and module namespace containment. |
| A service is not registered | [Service] class is outside the module namespace, the explicit contract is invalid, or no contract could be resolved. | Move it under the module namespace; verify contract resolution. |
| Hosted service registration fails a diagnostic | The hosted service uses Scoped/Transient scope. | Use Scope = ServiceScope.Singleton. |
| Generic service registration fails a diagnostic | [Service] is on a generic type or one nested in a generic type. | Register open generics manually for now. |
Validation attributes are not enforced (ELVAL002) | The request DTO carries DataAnnotations attributes but the compilation does not reference Elarion.Validation, or the host never calls AddElarionValidation(). | Reference Elarion.Validation from the application project and call services.AddElarionValidation() in the host. |
Event consumer never runs (ELEVT003) | The [ConsumeEvent] consumer's namespace is under no [AppModule], so it is not registered. | Move the consumer under a module's namespace so that module wires it. |
Handler-form consumer fails a diagnostic (ELEVT005) | A class-level [ConsumeEvent] is not on an IHandler<TEvent, Result<T>> (or IHandler<TEvent>) whose request is an event, or the handler returns a non-Unit Result<T> (event consumers — domain or integration — are fan-out only). | Implement the handler interface with an event request and return Result / Result<Unit>; for a typed reply use IHandlerSender/IHandler instead of the event bus. |
| RPC method is missing | The host lacks [GenerateModuleBootstrapper], the handler lacks [Handler], the handler's namespace is under no [AppModule], or the handler does not implement IHandler<TRequest, Result<TResponse>> (ELRPC002). | Add the trigger, move the handler under a module, add the marker, and implement the handler interface with a Result<T> response. |
| Module services do not run | The module is disabled via Modules:{Name}:Enabled=false, or [AppModule] is missing. | Check configuration and the module attribute. |
JSON-RPC and schema generation
| Symptom | Likely cause | Fix |
|---|---|---|
| Build-time schema generation does not run | ElarionJsonRpcGenerateSchema is not true, the schema package is not referenced, or …GenerateSchemaOnBuild was disabled. | Add the private package reference and set ElarionJsonRpcGenerateSchema=true, or invoke GenerateElarionJsonRpcSchema manually. |
| Schema generation fails after loading the app | Startup code before builder.Build() threw, or no frozen JsonRpcDispatcher was registered before build. | Register the dispatcher before builder.Build() and guard expensive startup work with JsonRpcSchemaGeneration.IsRunning. |
| Generated frontend RPC types are stale | rpc-schema.json was updated but the client generator was not rerun. | Export the schema and re-run the client generator. |
| Client generator fails on schema composition | The schema uses unsupported constructs such as oneOf, anyOf, or allOf. | Adjust the exported DTO shape, or extend the generator deliberately. |
Persistence and serialization
| Symptom | Likely cause | Fix |
|---|---|---|
Handler writes are not rolled back on failure; a warning says the in-memory (no-op) IUnitOfWork is in use | No durable unit of work is registered, so the transaction decorator falls back to the no-op default. | Reference Elarion.EntityFrameworkCore.UnitOfWork and call services.AddElarionUnitOfWork<AppDbContext>() (EF-free hosts: AddElarionSqlUnitOfWork()). |
System.Text.Json cannot serialize a DTO under AOT | The type is missing from a module JSON context. | Add [JsonSerializable(typeof(…))] to the relevant module context. |
DbSet<T> is missing for an entity | The entity has no [EntityConfiguration] (no IEntityTypeConfiguration<T> marked with [EntityConfiguration]), or its config's scope does not intersect the [GenerateDbSets("scope")] context. | Add an [EntityConfiguration] for the entity with a matching scope. |
A newly added same-assembly [EntityConfiguration] produces no DbSet<T> until the IDE is restarted | Not a generator bug — dotnet build and the CLI always reflect current source. Rider/ReSharper's source-generator host refreshes generated code lazily and can lag when a new [EntityConfiguration] is added in the same assembly as the [GenerateDbSets] context. (A configuration in a referenced assembly never looks stale because it only becomes available after you build the producing project, and that build refreshes the host.) | Build the project, or File → Invalidate Caches / Restart, to refresh the IDE host; the generated DbSet is correct once the host re-runs. |
DbUpdateConcurrencyException when replacing a tracked parent's children (Children.Clear() + Add(new … { Id = … })) — but only on a real database | EF's insert-vs-update heuristic misreads a set id on a key the model claims generated and issues an UPDATE for a row that was never inserted; the InMemory provider skips the affected-rows check, so tests stay green. On [GenerateDbSets] contexts the generated model already declares domain Guid PKs client-assigned, so this points at an entity outside the generated pass (foreign assembly, explicit ValueGeneratedOnAdd, or a hand-built context). | Declare the key ValueGeneratedNever() for client-assigned ids (see Entity identity), and cover replace-children flows with a real-database (Testcontainers) test. |
A PostgreSQL test suite's memory grows with every test; a heap dump shows thousands of NpgsqlDataSource instances | new NpgsqlConnection(connectionString) registers a data source per distinct connection string in Npgsql's static pool manager for the life of the process (also with Pooling=false, and also through ClearPool(new NpgsqlConnection(cs))); with one database per test every test adds entries. Undisposed NpgsqlDataSource.Create(…) instances are retained by Npgsql's static metrics list. | Open dedicated sessions with UnpooledNpgsqlSession.OpenAsync (Elarion.Sql.PostgreSql), dispose every data source you create, and clear pools with dataSource.Clear() on the data source the host uses. See One database per test. |
Inserts fail with a Guid.Empty duplicate key after upgrading | The entity relied on EF's client-side Guid generator (Id never set in code); the generated model now declares domain Guid PKs ValueGeneratedNever (ADR-0038). | Set ids at the creation site (Id = Guid.CreateVersion7(), recommended), or opt that entity back in with builder.Property(e => e.Id).ValueGeneratedOnAdd() — explicit configuration wins. |
Still stuck? Open an issue at github.com/swimmesberger/Elarion/issues with a minimal repro.