Diagnostics
Every generator and analyzer diagnostic Elarion can emit, what it means, and how to fix it.
Elarion's source generators and its module-boundary analyzer report problems as compiler diagnostics at build time, so unsupported patterns fail (or warn) deterministically instead of breaking at runtime. Each diagnostic below lists its id, severity, what it means, and how to fix it, grouped by prefix and sorted by id.
Looking for help by symptom (a build error message, a missing endpoint, a handler that never runs) rather than by id? Start with Troubleshooting, then come back here for the specific diagnostic.
ELRPC — JSON-RPC method generation
JSON-RPC method emission for [Handler] handlers. See
JSON-RPC.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELRPC001 | Warning | A [Handler] handler's namespace is not under any [AppModule]; it is registered unconditionally (not gated by a module feature flag). MCP reuses this id since it is built on [Handler]. | Move the handler under a module's namespace so its RPC/MCP registration is module-scoped and feature-gated. |
| ELRPC002 | Warning | A [Handler] handler does not implement IHandler<TRequest, TResponse> with a Result<T> response, so no RPC method can be generated. | Make the handler implement IHandler<TRequest, Result<TResponse>> (or the IHandler<T> sugar) so the request/response shape is resolvable. |
| ELRPC003 | Warning | Two handlers resolve to the same operation name on the shared bus (e.g. an inferred name colliding with another inferred or explicit one); one would silently win at runtime. | Give one handler an explicit [Handler("...")] name so every operation name is unique across the bus. |
ELHTTP — HTTP endpoint generation
Minimal-API endpoint emission for [HttpEndpoint] handlers. See
HTTP endpoints.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELHTTP001 | Warning | An [HttpEndpoint] handler does not implement IHandler<TRequest, TResponse> with a Result<T> response, so no endpoint can be generated. | Make the handler implement IHandler<TRequest, Result<TResponse>> (or the IHandler<T> sugar) so the request/response shape is resolvable. |
| ELHTTP002 | Warning | Two handlers map the same HTTP verb + route template (for example, the same GET /x). | Change one handler's verb or route template so each verb+route pair is unique. |
| ELHTTP003 | Warning | An [HttpEndpoint] handler's namespace is not under any [AppModule]; it is mapped unconditionally (not gated by any module feature flag) so it is never silently dropped. | Move the handler under a module's namespace so its endpoint is module-scoped and feature-gated. |
| ELHTTP004 | Warning | An [HttpEndpoint] has no explicit verb and its request implements neither ICommand (POST) nor IQuery (GET), so the verb cannot be inferred. | Specify an explicit verb on [HttpEndpoint], or make the request implement ICommand (POST) or IQuery (GET). |
| ELHTTP005 | Warning | A member of a member-wise-bound request DTO cannot be bound from its wire source (route/query/header/form), so no endpoint is generated. Supported member types are string, enums, IParsable<T> value types and their nullable forms, arrays of those from the query string, IFormFile/IFormFileCollection, and one [FromBody] member. | Change the member to a supported type, move complex payload data into a [FromBody] member, or bind the whole request from the JSON body (POST/PUT/PATCH without [From*]/file members). |
ELMCP — MCP customization
MCP tool customization for [McpHandler]. See MCP.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELMCP003 | Warning | A handler carries [McpHandler] (tool-name customization) but its [Handler] Transports flag does not include HandlerTransports.Mcp, so the customization has no effect. | Remove [McpHandler], or include HandlerTransports.Mcp in the handler's [Handler] Transports. |
ELEVT — Event consumers
Event-consumer discovery for [ConsumeEvent]. See Consuming events.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELEVT001 | Error | A method-form [ConsumeEvent] consumer method is declared on a class that is not annotated with [Service]. | Declare the consumer method on a [Service] class, or use the handler-form consumer instead. |
| ELEVT002 | Error | A method-form [ConsumeEvent] consumer has an unsupported signature. Event consumers are fan-out subscribers, so it must be an accessible, non-generic, non-static instance method accepting exactly one IDomainEvent/IIntegrationEvent parameter (optionally IEventContext and/or CancellationToken) and returning void/Task/ValueTask or the non-generic Result/Task<Result>/ValueTask<Result>. | Return void/Task/ValueTask or the non-generic Result (a fan-out subscriber); a Result<T> with a value is request/reply — call a handler by type with IHandlerSender/IHandler instead. |
| ELEVT003 | Warning | A [ConsumeEvent] consumer's namespace is not under any [AppModule], so it will not be registered. | Move the consumer under a module's namespace so it is wired by that module. |
| ELEVT005 | Error | A class-level [ConsumeEvent] is on a type that does not implement exactly one IHandler<TEvent> (or IHandler<TEvent, Result<Unit>>) whose request is an IDomainEvent/IIntegrationEvent; or it returns a non-Unit Result<T> (event consumers are fan-out subscribers); or the marker is on a non-handler. | Implement exactly one IHandler<TEvent> (response Result<Unit>) whose request is an IDomainEvent/IIntegrationEvent; for a typed reply use IHandlerSender/IHandler instead of the event bus. |
| ELEVT006 | Error | Multiple [ConsumeEvent] declarations produce the same durable consumer identity — a service may have only one consumer method with a given name for an event type, and the identity keys inbox dedup. | Rename or split one of the colliding [ConsumeEvent] declarations so each consumer identity is unique. |
ELACT — Actors
Actor facade generation for [Actor] classes. See Actors.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELACT001 | Error | An [Actor] type is static, abstract, generic, or nested, so the facade generator cannot wrap it. | Declare the actor as a non-static, non-abstract, non-generic top-level class. |
| ELACT002 | Error | A public method on an [Actor] class cannot be exposed through the facade: it is generic, returns something other than Task/Task<T>/ValueTask/ValueTask<T>, has ref/out/in or ref-struct parameters, or declares more than one CancellationToken. | Make the method a non-generic instance method returning Task/Task<T>/ValueTask/ValueTask<T> with plain by-value parameters and at most one CancellationToken. |
| ELACT003 | Warning | An [Actor] class's namespace is not under any [AppModule], so it will not be registered. | Move the actor under a module's namespace so it is wired by that module's Add{Module}Actors. |
| ELACT004 | Error | An [Actor] class declares conflicting keys — multiple IActorContext<TKey> constructor parameters with different key types, or an [Actor(KeyType = ...)] that contradicts the context parameter. | Declare a single IActorContext<TKey> constructor parameter (the idiomatic way to make an actor keyed), or make KeyType match it. |
| ELACT005 | Error | An [Actor] class does not have exactly one public constructor, so the generator cannot emit its activator. | Keep one public constructor carrying the activation dependencies (context + services). |
| ELACT006 | Warning | ConfigureAwait(false) (or a ConfigureAwaitOptions value without ContinueOnCapturedContext) inside a [Reentrant] actor escapes the exclusive scheduler: the rest of the method resumes off the actor's single-threaded context — and only when the await actually suspends, so the escape is latent. | Remove ConfigureAwait(false) from the actor's own methods, helpers, and lambdas. Libraries the actor calls may use it internally without harm (context capture is per-method). Non-reentrant actors are exempt — their guarantee comes from the mailbox loop, not a scheduler. |
| ELACT008 | Error | A keyed actor's [ConsumeEvent] method needs an actor key the generator cannot determine: the event has zero or more than one property assignable to the actor's key type, or the [ActorKey(...)] name does not resolve to such a property. | Add [ActorKey(nameof(TheEvent.KeyProperty))] naming the event property whose type is assignable to the actor's key type. Singleton actors need no key. See Actors and events. |
| ELACT009 | Error | [ConsumeEvent] is placed on a non-public [Actor] method. The generated relay reaches the actor through its public facade (the same call a hand-written relay makes), so the method must be on the facade. | Make the method public. |
| ELACT010 | Error | [ConsumeEvent] on an [Actor] method consumes an IDomainEvent. Domain events run inside the emitting command's transaction and scope; an actor runs in its own scope, so awaiting one abandons the same-transaction contract. | Consume an IIntegrationEvent instead (publish one to react to a domain change), or call the actor from the command's handler after its transaction commits. |
| ELACT012 | Error | An actor stream method (returning IAsyncEnumerable<T>, ADR-0052) takes a CancellationToken or carries [ConsumeEvent]. The turn token is a pooled CTS whose lifetime ends with the attach turn — using it inside the returned stream would observe a recycled token; a relay cannot await a stream. | Drop the CancellationToken parameter (the generated facade adds a trailing token that cancels the attach and, linked with the enumerator's token, the stream) and keep [ConsumeEvent] on Task-shaped methods. |
| ELACT013 | Error | An actor uses Placement = VirtualShards without a key. | Add an IActorContext<TKey> constructor parameter or Actor(KeyType = typeof(TKey)); virtual-shard placement is only meaningful for keyed actors. |
ELMOD — Module boundaries
App-module discovery and the ModuleBoundaryAnalyzer. See
Modules and Cross-module communication.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELMOD001 | Warning | Two [AppModule] types share the same namespace; generated transport handlers in that namespace are associated with the alphabetically first matching module (ambiguous ownership). | Give each module a distinct namespace so handler-to-module association is unambiguous. |
| ELMOD002 | Warning | Location-based: a type in one [AppModule] depends (via constructor parameter, field, or property) on a type inside another module — an entity, DTO, [Service], handler, or [EntityConfiguration] — that is not a published [ModuleContract]. Everything outside every module (the shared kernel and platform ports) is shareable; a shared-kernel entity is exempt because of where it lives, not because entities are special. | Reach the other module through its [ModuleContract] (used sparingly), a platform-capability port outside the modules, or move shared data/value types to the shared kernel. |
| ELMOD003 | Warning | A referenced assembly advertises an Elarion manifest whose schema version this generator does not understand; its manifest entries (modules, HTTP/RPC endpoints, permission-catalog entries) are skipped rather than misparsed. | Rebuild the referenced assembly against a matching Elarion version so its manifest schema aligns with the consuming generator. |
| ELMOD004 | Warning | A [ModuleEndpoints("Name")] class names a module no discovery produced; its endpoint hooks are skipped (mapping them ungated would defeat the feature gate the attribute reuses). | Fix the module name to match a discovered [AppModule], or reference the assembly that declares the module. |
| ELMOD005 | Warning | A [ModuleEndpoints] class declares neither a static MapEndpoints(IEndpointRouteBuilder) nor a static ConfigureEndpointGroup(IEndpointRouteBuilder) hook, so it contributes nothing. | Declare at least one of the two convention hooks (static, exactly one parameter), or remove the attribute. |
| ELMOD006 | Error | Two [AppModule] declarations share one module name. Module names key generated registration, gating, and hint names, so the duplicate would crash a generator or emit uncompilable code; only the ordinal-first type (by fully-qualified name) is generated. | Rename one of the modules so every [AppModule] name is unique across the application. |
ELAUTH — Authorization
Authorization-decorator attachment for [RequirePermission]/[RequireRole]/[RequireClaim]/[RequirePolicy]/[RequireResource]. See Authorization.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELAUTH001 | Error | A handler declares an authorization requirement but its response type does not implement IResultFailureFactory<T>, so the authorization check cannot short-circuit and would be silently skipped. | Return Result<T> or Result from the authorized handler so the check can short-circuit to Unauthorized/Forbidden. |
| ELAUTH002 | Error | A handler declares [RequireResource] with an Id path that does not resolve to a property on the request type. | Point [RequireResource] Id at an existing property path on the request, e.g. nameof(Request.Id) or a dotted path of existing properties. |
ELPOL — Authorization policies
Named-policy registration for [AuthorizationPolicy]. See Authorization.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELPOL001 | Error | [AuthorizationPolicy] is applied to a type that does not implement IAuthorizationPolicy. | Apply [AuthorizationPolicy("name")] only to a class implementing IAuthorizationPolicy. |
| ELPOL002 | Warning | An [AuthorizationPolicy] class's namespace is not under any [AppModule], so it is not auto-registered. | Move the [AuthorizationPolicy] class under an [AppModule] namespace so it is auto-registered. |
ELPERM — Permission catalog
Permission-catalog generation for [GeneratePermissionCatalog]. See Authorization.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELPERM001 | Warning | A handler declares authorization requirements but is not under any [AppModule] namespace, so they are not added to the runtime permission catalog. | Move the handler under an [AppModule] namespace so its [RequirePermission]/[RequireRole] enter the generated permission catalog. |
| ELPERM002 | Warning | Two permissions map to the same generated ElarionPermissions typed accessor; the second is omitted from the typed accessors (both remain in ElarionPermissions.All). | Rename one permission so the generated typed accessors do not collide. |
ELAPI — Module API facades
Typed in-process module API generation for [GenerateModuleApi] / [ModuleApi]. See
Cross-module communication.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELAPI001 | Error | An interface annotated with [GenerateModuleApi] is not declared partial, so the generated members cannot be emitted into it. | Declare the [GenerateModuleApi] interface as partial. |
| ELAPI002 | Error | A [GenerateModuleApi] interface is nested inside another type instead of being declared at namespace scope. | Move the [GenerateModuleApi] interface to namespace (top-level) scope. |
| ELAPI003 | Warning | A [GenerateModuleApi] interface's namespace is not under any [AppModule], so no handlers can be associated and the facade is left empty. | Move the facade interface under a module's namespace so its handlers can be discovered. |
| ELAPI004 | Error | A [GenerateModuleApi] facade maps more than one handler to the same method name (handlers share a type name); the duplicate handler is skipped. | Rename one of the conflicting handler types, or exclude one via [ModuleApi(Exclude = true)], so each facade method name is unique. |
ELEFC — Entity configuration
EF Core entity configuration discovery for [EntityConfiguration]. See
Entity Framework Core.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELEFC001 | Warning | An [EntityConfiguration] class implements no IEntityTypeConfiguration<T>, so no DbSet or configuration is generated for it. | Make the class implement at least one IEntityTypeConfiguration<T>, or remove the [EntityConfiguration] attribute. |
| ELEFC002 | Warning | Two [EntityConfiguration] entities share a short type name across namespaces and map to the same DbSet property name; the colliding DbSet is skipped (the first entity by name wins) to avoid a CS0102 duplicate member. | Rename one of the entities, or place it in a differently-scoped context so the two never map onto the same context. |
ELKEY — Keyset pagination
Keyset-definition generation for [Keyset<TEntity>]. See Pagination.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELKEY001 | Error | A [Keyset] declares a column name that does not match any property on the target entity; no keyset is generated. | Correct the keyset column name to match an existing entity property. |
| ELKEY002 | Error | A [Keyset] column maps to a property whose type is not supported for keyset pagination; no keyset is generated. | Use a supported (comparable) column type for keyset ordering, or remove that column from the keyset. |
| ELKEY003 | Error | A [Keyset] column maps to a nullable property; keyset columns must be non-nullable for deterministic ordering. No keyset is generated. | Make the keyset column non-nullable, or choose a different non-nullable column for the ordering. |
| ELKEY004 | Warning | The target entity has a primary-key property (a [Key]-annotated property, or the conventional Id/{EntityType}Id) that is not part of its [Keyset], so paging order may not be deterministic (no unique tiebreaker). | Append the key (or another unique column) to the keyset so ordering is deterministic and cannot skip or repeat rows. |
| ELKEY005 | Error | A [Keyset<TEntity>]-annotated class is nested or non-partial; the generator emits the IKeysetDefinition implementation into it and requires a non-nested partial class. | Declare the keyset class as a top-level (non-nested) partial class. |
ELSG — Services, scheduled jobs
Service registration and scheduled-job discovery. See Services and Scheduling.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELSG001 | Error | A [Service] class that is an IHostedService/BackgroundService is registered with a non-singleton ServiceScope. Hosted services must be singletons. | Set the service's scope to ServiceScope.Singleton on its [Service] attribute. |
| ELSG002 | Error | A [Service] declares an explicit contract type that the implementation class is not assignable to. | Make the implementation implement/inherit the declared contract, or correct the contract type on the [Service] attribute so it matches the implementation. |
| ELSG003 | Error | A [Service] is declared on a generic (open) class; the generator cannot register an open-generic service. | Make the service a non-generic (closed) class, or register the open generic manually outside the generator. |
| ELSG004 | Error | A [ScheduledJob] method has an unsupported signature. It must be accessible, non-generic, return Task/ValueTask, and accept only IScheduledJobContext and/or CancellationToken parameters. | Make the method accessible and non-generic, return Task/ValueTask, and take only IScheduledJobContext and/or CancellationToken. |
| ELSG005 | Error | A scheduled-job type is generic or is nested inside a generic type. | Make the job type non-generic and not nested in a generic type. |
| ELSG006 | Error | A runtime-schedulable job type does not implement exactly one IScheduledJob<TPayload> interface (zero or more than one). | Implement exactly one IScheduledJob<TPayload> on the job type. |
| ELSG007 | Error | Two or more scheduled jobs declare the same job name. Names must be unique. | Give each scheduled job a unique name. |
| ELSG008 | Error | A scheduled job's schedule is invalid: it must declare exactly one of FixedRate, FixedDelay, Cron, or InitialDelay-only one-time scheduling, and InitialDelay/RunOnStart cannot be combined with Cron. | Specify exactly one schedule mode, and do not combine InitialDelay/RunOnStart with Cron. |
| ELSG009 | Error | A scheduled job's MaxConcurrentRuns is negative (must be 0 or greater). | Set MaxConcurrentRuns to 0 (unbounded) or a positive integer. |
| ELSG010 | Warning | A [ScheduledJob] type's namespace is not under any [AppModule]; under a module-bootstrapper host it will not be registered. | Move the job under a module's namespace so it is wired by that module's gated registration. |
| ELSG011 | Error | A [Handler(Scope = ServiceScope.Singleton)] handler has a constructor dependency that is registered scoped or transient — a singleton handler is constructed once from the root provider, so the dependency would be captured for the application lifetime. | Make the dependency singleton or keep the handler scoped. |
| ELSG012 | Error | A singleton handler's constructor dependency cannot be verified as singleton at compile time (unknown registration, keyed service, or a [Service] contract declared with conflicting scopes). There is deliberately no escape hatch: a captive scoped dependency must be impossible to ship. | Annotate the dependency's implementation with [Service(Scope = ServiceScope.Singleton)] in this assembly, or keep the handler scoped. |
| ELSG013 | Error | A singleton handler's pipeline attaches a scope-dependent feature — [Idempotent] (or the default integration-event inbox), [Auditable], [Cacheable]/[CacheInvalidate], an authorization requirement, a [FeatureGate], request validation, a feature-variant dependency, or a custom decorator with a per-dispatch dependency (the transaction decorator's IUnitOfWork included). These resolve per-dispatch services when the chain is built once from the root provider. | Remove the feature or keep the handler scoped. Singleton handlers pair with singleton dependencies and [Resilient] (the resilience runner is a singleton). |
| ELSG014 | Error | A [GenerateContractSetRegistration] method names a contract that is not an interface or abstract class (or is an open generic). A contract set composes implementations of a contract; a concrete or open-generic type cannot be one. The method is still implemented (empty) so this is the only error you see. | Point the attribute at the seam's interface or abstract base class. |
| ELSG015 | Warning | A declared contract set has no implementations in this assembly — usually a typo'd contract or a refactor casualty. The generated method still exists and registers nothing; the registry consuming the set decides whether empty is fatal. | Check the contract type, or remove the declaration if the seam is gone. |
| ELSG016 | Error | The same contract is declared by more than one [GenerateContractSetRegistration] method in this assembly, which would be ambiguous. The first declaration (by file position) gets the real implementation; later ones are implemented empty. | Keep exactly one contract-set method per contract per assembly. |
| ELSG017 | Error | A generic class implements a declared contract-set contract; the generator cannot register an open-generic implementation (mirrors ELSG003). | Make the implementation a non-generic (closed) class, or register it manually. |
| ELSG018 | Warning | A contract-set implementation also carries [Service] with the same contract among its resolved contracts — it would register twice, once module-gated and once unconditionally. | Pick one mechanism per contract: drop [Service] (or pin its explicit contracts elsewhere), or stop composing the contract as a set. |
| ELSG019 | Error | A [GenerateContractSetRegistration] attribute sits on a method that is not a valid contract-set declaration. The generator fills in a partial method body, so the shape is fixed: static partial IServiceCollection Name(this IServiceCollection services) on a non-generic static partial class, with no hand-written implementation part. | Match the required signature — static, partial without an implementation, an extension method taking and returning IServiceCollection, non-generic. |
ELPIPE — Decorator pipelines
Decorator attachment in handler registration. See Decorator pipelines.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELPIPE001 | Error | A decorator declares an AppliesTo attachment-predicate method that is not public, so the generated registration cannot call it to decide attachment. | Declare the predicate as public static bool AppliesTo(HandlerMetadata handler). |
| ELPIPE002 | Error | A decorator declares an AppliesTo method with an unsupported signature (e.g. the older AppliesTo(System.Type request)). | Use the one supported form, public static bool AppliesTo(HandlerMetadata handler); for request-based checks use handler.RequestType. |
| ELPIPE003 | Error | A handler carries [Resilient] but consumes a domain event (request : IDomainEvent), which is dispatched inline within the publisher's transaction; a Polly retry would re-apply the handler's tracked mutations inside that live transaction. | Remove [Resilient] from the domain-event consumer. Integration-event consumers run on a fresh post-commit scope and may be resilient. |
| ELPIPE004 | Warning | A handler whose request is an ICommand carries [Resilient] but not [Idempotent], and the referenced policy is not provably retry-free. A retrying policy can execute the command twice: the resilience decorator wraps the transaction, whose finalizing commit deliberately runs uncancellable — a per-attempt timeout abandons the attempt without waiting for it, the in-flight commit still completes in the background, the timeout exception is not a cancellation so the retry fires, and the command executes and commits again. | Add [Idempotent] so a retry replays the first committed outcome instead of re-executing. A timeout-only [ResiliencePolicy] declared in the same assembly is recognized and does not warn; suppress the diagnostic if the policy is declared elsewhere and never retries. |
ELRES — Resilience policies
Resilience-policy registration for [ResiliencePolicy]. See Resilience.
The ELRES001/ELRES002 ids are shared by two unrelated analyzers — the resilience-policy
analyzer here (category Elarion.Abstractions.Resilience) and the EF Core
[ResourceFilter] family below (category
Elarion.EntityFrameworkCore). Disambiguate by the diagnostic's category: only one of the two
ever fires for a given declaration.
| Id | Severity | Category | Meaning | Fix |
|---|---|---|---|---|
| ELRES001 | Error | Elarion.Abstractions.Resilience | A [ResiliencePolicy] is invalid (the message includes the specific reason, for example bad retry/timeout option values). | Correct the resilience policy configuration per the reason in the diagnostic message. |
| ELRES002 | Error | Elarion.Abstractions.Resilience | Two or more resilience policies declare the same policy name. | Give each resilience policy a unique name. |
ELRES — Resource filter (EF Core)
Data-level authorization list-filter generation for [ResourceFilter<TEntity>]. See
Resource authorization. These EF Core ids share the
ELRES001/ELRES002 numbers with the resilience analyzer above but use a distinct category
(Elarion.EntityFrameworkCore).
| Id | Severity | Category | Meaning | Fix |
|---|---|---|---|---|
| ELRES001 | Error | Elarion.EntityFrameworkCore | A [ResourceFilter] rule references a property name that does not match any property on the target entity. | Reference an existing entity property in the [ResourceFilter] rule. |
| ELRES002 | Error | Elarion.EntityFrameworkCore | A [ResourceFilter] rule references a property whose type is not supported for the generated predicate. | Use a supported property type in the [ResourceFilter] rule. |
| ELRES003 | Error | Elarion.EntityFrameworkCore | A [ResourceFilter<TEntity>]-annotated class is nested or non-partial. | Declare the [ResourceFilter<T>] class as a top-level partial class. |
| ELRES004 | Error | Elarion.EntityFrameworkCore | A [ResourceFilter] declares no rules, so no authorization predicate can be generated. | Add at least one rule (OwnerProperty/TenantProperty/Shared) to the [ResourceFilter]. |
| ELRES005 | Error | Elarion.EntityFrameworkCore | A [ResourceFilter] Shared rule omits ResourceTypeName, which the grants EXISTS subquery requires. | Set ResourceTypeName on the [ResourceFilter] Shared rule. |
ELCACHE — Handler caching
Handler caching configuration for [Cacheable] / [CacheInvalidate]. See Caching.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELCACHE001 | Error | A handler is annotated with both [Cacheable] and [CacheInvalidate]; these are mutually exclusive. | Apply only one of [Cacheable] or [CacheInvalidate] to the handler. |
| ELCACHE002 | Error | A handler's cache configuration defines no non-empty cache tag (at least one is required). | Define at least one non-empty cache tag on the handler's caching attribute. |
| ELCACHE003 | Error | A handler declares a cache tag that fails validation (invalid format/value). | Replace the invalid cache tag with a valid tag value. |
| ELCACHE004 | Error | A cacheable handler's cache duration is not positive (must define a positive duration). | Set a positive cache duration on the handler's [Cacheable] configuration. |
| ELCACHE005 | Error | A handler carries [Cacheable] but consumes an event (request : IDomainEvent/IIntegrationEvent); caching a fan-out consumer's Result<Unit> would silently skip the side effect on a legitimate re-delivery. | Remove [Cacheable] from the event consumer. ([CacheInvalidate] on a consumer is legitimate — reacting to an event by evicting caches — and is left attached.) |
| ELCACHE006 | Error | A request property participating in the cache key has a type with no stable key formatting (it would fall back to object.ToString(), colliding across values and risking a cross-request cache leak). | Use a scalar key property (primitive, string, char, bool, Guid, enum, DateTime/DateTimeOffset/DateOnly/TimeOnly/TimeSpan, decimal, or a Nullable of those), or restrict the key with [Cacheable(KeyProperties = ...)]. |
| ELCACHE007 | Error | A [Cacheable(KeyProperties = ...)] names a property that is not a public instance property on the request type, so it would be silently dropped from the key. | Reference an existing request property, e.g. nameof(Request.Id). |
ELFEAT — Feature gates
Feature-gate decorator attachment for [FeatureGate]. See Feature flags.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELFEAT001 | Error | A handler declares a [FeatureGate] but its response type does not implement IResultFailureFactory<T>, so the gate cannot short-circuit and would be silently skipped. | Return Result<T> or Result from the gated handler so the gate can short-circuit to AppError.NotFound. |
| ELFEAT002 | Warning | A handler declares a [FeatureGate] with no feature name (or a blank one); the gate has no effect. | Pass at least one non-blank feature name to [FeatureGate], or remove it. |
ELVAL — Request validation
Validation-decorator attachment for handlers whose request DTO carries DataAnnotations validation attributes. See Validation.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELVAL001 | Error | A handler's request type carries validation attributes but its response type does not implement IResultFailureFactory<T>, so the validation check cannot short-circuit and would be silently skipped (mirrors ELAUTH001). | Return Result<T> or Result from the handler so the validation decorator can short-circuit to AppError.Validation. |
| ELVAL002 | Warning | A handler's request type carries validation attributes but the compilation does not reference Elarion.Validation; the constraints are exported to every schema surface but not enforced at runtime. | Reference Elarion.Validation and call AddElarionValidation() in the host, or remove the validation attributes — the gap must be a visible choice. |
ELREQ — Self-typed request markers
Consistency checks for the self-typed request markers (IRequest<TSelf, TResponse>,
ICommand<TSelf, TResponse>, IQuery<TSelf, TResponse>, IStreamRequest<TSelf, TItem>) that drive
inferred dispatch (ADR-0065). Both mistakes compile but would otherwise surface only at runtime — as an
invalid cast in the inferred dispatch overloads or as failed handler resolution. See
Command vs. query.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELREQ001 | Error | A type implements a self-typed marker whose TSelf names a different, unrelated type (usually a copy-paste slip); inferred dispatch of this request would throw InvalidCastException. | Make the marker name the implementing type itself: record Query(…) : IQuery<Query, Response>. |
| ELREQ002 | Warning | A handler pairs a request with a response that differs from the TResponse the request's marker declares, so inferred dispatch resolves a different IHandler<,> closure and will not find this handler. | Align the marker's TResponse with the handler's Result<T> (or vice versa). A deliberate second handler with a different response for the same request stays reachable through the explicit-generic overloads; suppress the warning there. |
| ELREQ003 | Warning | A stream handler's item type differs from the TItem the request's IStreamRequest<TSelf, TItem> marker declares, so inferred stream dispatch will not find this handler. | Align the marker's TItem with the stream handler's item type (or vice versa). |
ELVAR — Variant services
Variant-service registration for [FeatureVariant] and [ConfigurationVariant]. See
Feature flags. (ELVAR002 is intentionally unused.)
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELVAR001 | Error | A variant key is declared more than once for the same contract. | Give each implementation of a contract a distinct Variant/Value (one default plus unique named variants). [ConfigurationVariant] values match case-insensitively, so values differing only by case also collide. |
| ELVAR003 | Warning | A contract has no default implementation (one declared without a Variant/Value), so resolution when nothing matches will fail. | Add one implementation with no Variant/Value set as the default. |
| ELVAR004 | Error | A contract is bound to more than one selector; a contract maps to exactly one feature or configuration key. | Use a single feature name (or configuration key) across all variant implementations of a contract. |
| ELVAR005 | Warning | A variant service declares a blank feature name. | Pass a non-blank feature name to [FeatureVariant]. |
| ELVAR006 | Error | A generic variant-service implementation is not supported. | Make the variant [Service] implementation non-generic (concrete). |
| ELVAR007 | Error | A variant service is not also annotated with [Service]; [FeatureVariant]/[ConfigurationVariant] is a modifier on a service registration (the [Service] declares the service and its lifetime). | Add [Service] alongside the variant attribute on the implementation class. |
| ELVAR008 | Error | A contract is bound by both [FeatureVariant] and [ConfigurationVariant]; a contract is selected by exactly one axis. | Pick one axis per contract: per-user feature allocation, or a global configuration value. |
| ELVAR009 | Warning | A variant service declares a blank configuration key. | Pass a non-blank configuration key to [ConfigurationVariant]. |
| ELVAR010 | Warning | Two variant selectors or values map to the same ElarionVariants accessor name (PascalCase collision); the second is omitted from the typed accessors, while every entry remains in the data surfaces (All/ByKey/…). | Rename one of the colliding selector keys or values so the PascalCased accessors differ. |
ELRG — Resource grants
Resource-grants generation for [GenerateElarionResourceGrants]. See
Resource authorization.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELRG001 | Error | A context is annotated with [GenerateElarionResourceGrants] but not [GenerateDbSets], so the resource-grants DbSet and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionResourceGrants]. |
ELIDN — Identity model generation
Identity-model generation for [GenerateElarionIdentity]. See Identity.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELIDN001 | Error | A context is annotated with [GenerateElarionIdentity] but not [GenerateDbSets], so the Identity DbSets and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionIdentity]. |
ELIDEM — Idempotency
Idempotency-decorator attachment for [Idempotent]. See Idempotency.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELIDEM001 | Error | A handler declares [Idempotent] but its response type does not implement IResultFailureFactory<T>, so the idempotency decorator cannot synthesize the 400/409/422/replay outcomes and would be silently skipped. | Return Result<T> or Result from the handler so the idempotency decorator can short-circuit. |
| ELIDEM002 | Warning | A handler declares [Idempotent] but its request type is not an ICommand; idempotency only applies to state-changing commands, so the attribute has no effect. | Make the request implement ICommand, or remove [Idempotent]. |
| ELIDEM003 | Error | A handler's [Idempotent] declares a non-positive RetentionHours. | Set a positive RetentionHours on [Idempotent]. |
| ELIDEM004 | Warning | A handler carries both [Idempotent] and [Cacheable]; caching is for queries, idempotency for commands. | Apply only one of [Idempotent] or [Cacheable] to the handler. |
ELINBX — Inbox (idempotent event consumers)
Inbox attachment for handler-form integration-event consumers (on by default; [AllowDuplicates] opts out).
See Handling duplicates.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELINBX001 | Warning | A handler declares [AllowDuplicates] but its request type is not an IIntegrationEvent; only handler-form integration-event consumers have a default-on inbox to opt out of (domain events are exactly-once by atomicity), so the attribute has no effect. | Remove [AllowDuplicates], or make the handler consume an IIntegrationEvent. |
ELIDEMEF — Idempotency keys (EF Core)
Idempotency-keys generation for [GenerateElarionIdempotencyKeys]. See Idempotency.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELIDEMEF001 | Error | A context is annotated with [GenerateElarionIdempotencyKeys] but not [GenerateDbSets], so the idempotency-keys DbSet and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionIdempotencyKeys]. |
ELAUD — Audit trail (EF Core)
Audit-log generation for [GenerateElarionAuditing]. See Audit trail.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELAUD001 | Error | A context is annotated with [GenerateElarionAuditing] but not [GenerateDbSets], so the audit-log DbSet and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionAuditing]. |
ELOBX — Transactional outbox
Outbox-table generation for [GenerateElarionOutbox]. See
Event bus backends.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELOBX001 | Error | A context is annotated with [GenerateElarionOutbox] but not [GenerateDbSets], so the outbox DbSet and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionOutbox]. |
ELSCH — Scheduler claims (EF Core)
Scheduler-claims generation for [GenerateElarionSchedulerClaims]. See
Scheduling.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELSCH001 | Error | A context is annotated with [GenerateElarionSchedulerClaims] but not [GenerateDbSets], so the scheduler-claims DbSet and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionSchedulerClaims]. |
ELSET — Settings (EF Core)
Settings-table generation for [GenerateElarionSettings]. See Settings.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELSET001 | Error | A context is annotated with [GenerateElarionSettings] but not [GenerateDbSets], so the settings DbSet and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionSettings]. |
ELBLB — Blob storage and staged uploads (PostgreSQL)
Blob-table generation for [GenerateElarionBlobStorage] and staged-upload-table generation for
[GenerateElarionStagedUploads]. See Blob storage and
Blob uploads.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELBLB001 | Error | A context is annotated with [GenerateElarionBlobStorage] but not [GenerateDbSets], so the blob DbSet and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionBlobStorage]. |
| ELBLB002 | Error | A context is annotated with [GenerateElarionStagedUploads] but not [GenerateDbSets], so the staged-upload DbSet and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionStagedUploads]. |
ELASN — Actor snapshots (PostgreSQL)
Snapshot-table generation for [GenerateElarionActorSnapshots]. See
Actor state and placement.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELASN001 | Error | A context is annotated with [GenerateElarionActorSnapshots] but not [GenerateDbSets], so the actor-snapshot DbSet and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionActorSnapshots]. |
ELROLE — Role leases (PostgreSQL)
Role-lease-table generation for [GenerateElarionRoleLeases] (ADR-0049). See
Actors — multiple instances and single-homing.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELROLE001 | Error | A context is annotated with [GenerateElarionRoleLeases] but not [GenerateDbSets], so the role-lease DbSet and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionRoleLeases]. |
ELDEV — Device identity (EF Core)
Device-identity-table generation for [GenerateElarionDeviceIdentity] (ADR-0054). See
Device identity.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELDEV001 | Error | A context is annotated with [GenerateElarionDeviceIdentity] but not [GenerateDbSets], so the device identity DbSets and model-configuration seam are not generated. | Add [GenerateDbSets] to the context alongside [GenerateElarionDeviceIdentity]. |
ELCEV — Client events
Generated client-event topic registration for IClientEvent contracts. See
Client events.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELCEV001 | Warning | An IClientEvent contract's namespace is not under any [AppModule], so no topic is registered for it. | Move the contract under an [AppModule] namespace, or register the topic manually via AddElarionClientEvents. |
| ELCEV002 | Error | Two IClientEvent contracts resolve to the same topic name; the colliding topics are withheld. | Rename one contract, or disambiguate with an explicit [ClientEvent("…")] name. |
| ELCEV003 | Warning | The compilation declares IClientEvent contracts but does not reference Elarion.ClientEvents, so no topics are registered. | Reference Elarion.ClientEvents (and map the endpoint from Elarion.ClientEvents.AspNetCore), or remove the contracts. |
ELSQL — SQL row mapping
Mapper generation for [SqlRecord] types (ADR-0058). See SQL mapping.
There is deliberately no runtime fallback behind these errors: an unmapped type has no mapper to call,
so every gap surfaces at build time.
| Id | Severity | Meaning | Fix |
|---|---|---|---|
| ELSQL001 | Error | A property's type is not a supported SQL column type. | Use a supported primitive, serialize the property as JSON with [SqlJson], or exclude it with [SqlIgnore]. |
| ELSQL002 | Error | Two properties map to the same column name — a silent last-wins would corrupt reads. | Rename one property or give it a distinct [SqlColumn("…")]. |
| ELSQL003 | Error | A required property is excluded from mapping ([SqlIgnore] or non-writable), so the generated mapper could not construct the row. | Map the property or drop required. |
| ELSQL004 | Error | A [SqlRecord] type has no mapped columns. | Add at least one readable, writable property of a supported type. |
| ELSQL005 | Error | A [SqlRecord] type is nested, generic, or abstract. | Move the row type to a top-level, non-generic, concrete class, record, or struct. |
| ELSQL006 | Error | No usable constructor: no accessible parameterless constructor with writable mapped properties, and no constructor whose parameters match mapped columns by name. | Add a parameterless constructor with set/init properties, or use a positional record. |
| ELSQL007 | Error | A property carries [SqlColumn]/[SqlJson] but has no accessible setter or initializer. | Add set/init, or remove the mapping attribute (derived members are skipped silently). |
| ELSQL010 | Error | A [SqlRecord] type is not partial, so the self-mapping members (ISqlRecord<T>.SqlMapper, Table, Select) cannot be generated. | Add the partial modifier. (The mapper still generates, so the explicit-mapper overloads keep working meanwhile.) |
| ELSQL011 | Error | A [SqlRecord] type declares a member named SqlMapper, Table, or Select, colliding with a generated self-mapping member. | Rename the member; a mapped column can keep its SQL name via [SqlColumn("…")]. |