Diagnostics
Pragmatic Design emits compile-time diagnostics prefixed with PRAG to guide correct usage of its attributes, detect configuration issues, and surface framework misuse early. Diagnostics are produced by the unified Source Generator and the module Analyzers.
All diagnostics are visible in the IDE (Rider, VS, VS Code) and in dotnet build output.
Severity legend
Section titled “Severity legend”- Error — compilation fails; the construct is invalid or will not work at runtime.
- Warning — compilation succeeds; the construct is likely wrong or will behave surprisingly.
- Info — hint; the construct is unusual but not incorrect.
ID ranges by module
Section titled “ID ranges by module”Each module owns a dedicated PRAG range. To look up the exact title and message format for a specific code, open the corresponding Diagnostics.cs file linked below.
Representative examples
Section titled “Representative examples”The diagnostics below are the ones new users hit most often. Full titles and messages come straight from the descriptor source files.
Actions — PRAG0400 (Error)
Section titled “Actions — PRAG0400 (Error)”Action class must be partial — “Action class ‘{0}’ must be declared as partial”.
The Source Generator emits the invoker inside the same class as a partial declaration. A non-partial action class cannot receive the generated code.
// ❌ PRAG0400[DomainAction]public class PlaceOrder : IDomainAction<OrderResult> { ... }
// ✅[DomainAction]public partial class PlaceOrder : IDomainAction<OrderResult> { ... }Persistence — PRAG0680 (Warning)
Section titled “Persistence — PRAG0680 (Warning)”Entity instantiated with
newbypasses the generated factory.
[Entity<TKey>] generates a Create(...) static factory that populates PersistenceId, audit fields, and raises domain events. Using new Entity() directly skips that path.
// ❌ PRAG0680var order = new Order { ... };
// ✅var order = Order.Create(...);Composition — PRAG1602 (Error)
Section titled “Composition — PRAG1602 (Error)”Boundary not found — a
[BelongsTo<TBoundary>]or[Module<TModule>]reference points to a type that does not exist or is not a[Boundary]/[Module].
Composition — PRAG1642 (Warning)
Section titled “Composition — PRAG1642 (Warning)”Lifetime mismatch — a scoped or transient dependency is injected into a singleton. The captured instance will be pinned for the lifetime of the app.
Composition — PRAG1647 (Warning)
Section titled “Composition — PRAG1647 (Warning)”Optional injection —
[Inject]defaults toRequired = false, so a missing service is injected asnulland may throw aNullReferenceExceptionat first use.
[Inject] opts into optional resolution by default. The analyzer surfaces this so the nullable contract is visible; set Required = true to fail fast at startup, or keep Required = false explicitly to acknowledge it.
// ❌ PRAG1647 — silent null if the service is unregistered[Inject]public IClock? Clock { get; set; }
// ✅ fail fast at startup[Inject(Required = true)]public IClock Clock { get; set; } = null!;Messaging — PRAG0800-0831
Section titled “Messaging — PRAG0800-0831”Handler pipeline, outbox, saga, topology, and request/response diagnostics. Common codes:
PRAG0801— Handler class must be partialPRAG0810— Saga state property missingPRAG0815—[CompensateWith<T>]target must implementICompensator<TState>PRAG0830,PRAG0831— request/response handler diagnostics (the highest Messaging codes)
Jobs — PRAG2500-2505
Section titled “Jobs — PRAG2500-2505”PRAG2500—[Job]class must implementIJoborIJob<T>PRAG2501—[RecurringJob]has an invalid cron expressionPRAG2502— Job class must bepartialPRAG2503— Duplicate recurring job IDPRAG2504—[Retry]withMaxAttempts <= 0PRAG2505—[Continuation<T>]target must implementIJobPRAG2506— additional Jobs diagnostic (see source)
Traits — PRAG2600-2601
Section titled “Traits — PRAG2600-2601”PRAG2600,PRAG2601— trait wiring diagnostics for[HasComments]/[HasTags]/[HasNotes]/[HasAttachments].PRAG2601warns when a trait is applied without a[Resource]on the parent — the trait entity and actions are still generated, but no endpoints are, because the route segment comes from[Resource].
Resource — PRAG2602-2605
Section titled “Resource — PRAG2602-2605”PRAG2602—[Resource]segment must be kebab-casePRAG2603— duplicate resource segment within the same boundaryPRAG2605—Capabilitiesset withoutRead(a Read capability is recommended)
ValueObject — PRAG2700-2702
Section titled “ValueObject — PRAG2700-2702”PRAG2700—[ValueObject]record must bepartialPRAG2701—[ValueObject]should declare aValidate(...)method soCreate(...)can be generated
Configuration — PRAG2000-2050
Section titled “Configuration — PRAG2000-2050”PRAG2000,PRAG2001,PRAG2050—[Configuration]options-binding diagnostics.
Manifest — PRAG9001 (Warning)
Section titled “Manifest — PRAG9001 (Warning)”Emitted when the inline manifest generator (run from EndpointsFeature) fails to build the API manifest for an assembly. The build still succeeds; OpenAPI/client-generation enrichment for that assembly is skipped.
Suppressing diagnostics
Section titled “Suppressing diagnostics”All diagnostics can be suppressed via standard C# mechanisms:
<!-- Project-wide: .csproj --><PropertyGroup> <NoWarn>$(NoWarn);PRAG0402</NoWarn></PropertyGroup>// Per-file or per-line:#pragma warning disable PRAG0402 // Action has no injectable dependenciespublic partial class UtilityAction : VoidDomainAction { ... }#pragma warning restore PRAG0402Prefer a targeted suppression with a comment explaining why — the diagnostics are opinionated guardrails, and suppressing one is usually a signal that the framework assumption doesn’t fit your case.
Contributing new diagnostics
Section titled “Contributing new diagnostics”New diagnostics must pick an ID inside an unused range of their module and follow the convention:
- Descriptor in
{Module}Diagnostics.cswithCategory = "Pragmatic.{Module}" - Title as a one-line imperative sentence
MessageFormatwith positional{0}/{1}placeholders (avoid string interpolation)- Severity: Error only if the generated code won’t work; Warning for “will work but surprising”; Info for hints
- A dedicated test case in the module’s
Generator/orAnalyzers/test project
See docs/completed/naming-sg.md for the full SG naming and convention playbook.