Diagnostics & Troubleshooting
Complete reference for all PRAG diagnostics emitted by the Pragmatic source generators and analyzers.
1. Overview
Section titled “1. Overview”What Are PRAG Diagnostics?
Section titled “What Are PRAG Diagnostics?”Pragmatic.Design uses Roslyn source generators and Roslyn analyzers to generate code at compile time.
When the SG encounters invalid configurations, missing attributes, or suboptimal patterns, it emits
diagnostics with IDs in the PRAG#### format.
These diagnostics appear:
- In the Error List window in Visual Studio / Rider
- In the build output of
dotnet build - In CI logs
Severity Levels
Section titled “Severity Levels”| Severity | Build Effect | When Used |
|---|---|---|
| Error | Fails the build | Invalid attribute usage, missing requirements, incompatible code |
| Warning | Build succeeds with warning | Suboptimal patterns, deprecated usage, performance concerns |
| Info | Build succeeds silently (unless verbose) | Suggestions, hints, informational messages |
| Hidden | Not shown in output | Refactoring opportunities, code fix triggers |
How to Suppress a Diagnostic
Section titled “How to Suppress a Diagnostic”If you intentionally want to suppress a diagnostic, use any of these approaches:
<!-- In .csproj — suppress globally --><PropertyGroup> <NoWarn>$(NoWarn);PRAG0303</NoWarn></PropertyGroup>// In code — suppress locally#pragma warning disable PRAG0303public partial class MyDto { /* ... */ }#pragma warning restore PRAG0303# In .editorconfig — configure severity[*.cs]dotnet_diagnostic.PRAG0303.severity = noneTip: Avoid suppressing Error-level diagnostics. They indicate real problems that will cause incorrect or missing generated code.
2. Quick Reference Table
Section titled “2. Quick Reference Table”Source Generator Diagnostics
Section titled “Source Generator Diagnostics”| ID | Module | Severity | Title | Quick Fix |
|---|---|---|---|---|
| PRAG0200 | Validation | Error | Type must be partial | Add partial keyword |
| PRAG0201 | Validation | Error | Validator must implement IValidator<T> | Implement the interface |
| PRAG0202 | Validation | Warning | Property has validation but type not partial | Add partial keyword |
| PRAG0203 | Validation | Error | Comparison property not found | Check nameof() reference |
| PRAG0204 | Validation | Warning | ValidateElements on non-collection | Remove attribute or change type |
| PRAG0205 | Validation | Error | ValidateElements element type not validatable | Add validation attrs to element type |
| PRAG0206 | Validation | Warning | Validator without validated type | Add ISyncValidator to target |
| PRAG0209 | Validation | Warning | Incompatible comparison types | Use compatible property types |
| PRAG0300 | Mapping | Error | Type must be partial | Add partial keyword |
| PRAG0301 | Mapping | Error | Source type not found | Check type reference exists |
| PRAG0302 | Mapping | Error | Property not found on source | Check property name spelling |
| PRAG0303 | Mapping | Warning | No matching source property | Add [MapProperty] or [MapIgnore] |
| PRAG0304 | Mapping | Error | Incompatible types | Add converter or change types |
| PRAG0305 | Mapping | Error | Converter must implement IValueConverter | Implement correct interface |
| PRAG0306 | Mapping | Error | Converter needs parameterless constructor | Add public parameterless ctor |
| PRAG0307 | Mapping | Warning | Required property not mapped | Map the property or remove required |
| PRAG0309 | Mapping | Error | Nested type missing MapFrom | Add [MapFrom<T>] to nested type |
| PRAG0310 | Mapping | Error | GenerateProjection requires MapFrom | Add [MapFrom<T>] first |
| PRAG0311 | Mapping | Warning | Method cannot translate to SQL | Simplify expression for EF Core |
| PRAG0312 | Mapping | Error | Nested projection missing attribute | Add [GenerateProjection] |
| PRAG0313 | Mapping | Info | Circular reference detected | Informational — instance tracking used |
| PRAG0314 | Mapping | Error | Conflicting MapIgnore and MapProperty | Remove one attribute |
| PRAG0315 | Mapping | Error | Nested DTO property mismatch | Align DTO type with navigation |
| PRAG0316 | Mapping | Error | No suitable constructor | Add constructor matching init-only props |
| PRAG0317 | Mapping | Error | Nullable to non-nullable without Default | Add Default = ... to attribute |
| PRAG0319 | Mapping | Warning | CustomizeMapping ignored in Projection | Use non-projection mapping |
| PRAG0320 | Mapping | Warning | MapConverter not supported in Projection | Remove converter for projection |
| PRAG0321 | Mapping | Info | Format not supported in Projection | Simplify format for SQL |
| PRAG0322 | Mapping | Info | Complex dictionary not supported | Use [MapIgnore] |
| PRAG0323 | Mapping | Warning | Ambiguous mapping | Add explicit [MapProperty] |
| PRAG0324 | Mapping | Info | ID property excluded from ToEntity | Use [MapProperty] to include |
| PRAG0325 | Mapping | Hidden | Source property not mapped to DTO | Add property or [MapperIgnoreSource] |
| PRAG0400 | Actions | Error | Action class must be partial | Add partial keyword |
| PRAG0401 | Actions | Error | Must inherit DomainAction base | Inherit DomainAction<T> or VoidDomainAction |
| PRAG0402 | Actions | Info | No injectable dependencies | Informational — may be intentional |
| PRAG0404 | Actions | Error | LoadEntity ID property not found | Add the ID property to the action |
| PRAG0405 | Actions | Error | LoadEntity key type not determined | Ensure entity has [Entity<TKey>] |
| PRAG0406 | Actions | Error | Boundary must be partial | Add partial to [Boundary] class |
| PRAG0407 | Actions | Error | Boundary must be in namespace | Move class into a namespace |
| PRAG0409 | Actions | Error | Mutation must inherit Mutation<T> | Inherit Mutation<TEntity> |
| PRAG0410 | Actions | Error | Mutation mode not determined | Add [Mutation(Mode = ...)] or use Create/Update prefix |
| PRAG0412 | Actions | Warning | SubBoundary nesting too deep | Flatten namespace structure |
| PRAG0413 | Actions | Info | SubBoundary inferred from namespace | Informational |
| PRAG0500 | Endpoints | Error | Endpoint must be partial | Add partial keyword |
| PRAG0501 | Endpoints | Error | Must inherit Endpoint base | Inherit Endpoint<T> or VoidEndpoint |
| PRAG0502 | Endpoints | Error | Route is required | Add route to [Endpoint] attribute |
| PRAG0503 | Endpoints | Error | Too many error types | Reduce to 6 or fewer |
| PRAG0504 | Endpoints | Warning | Route parameter not found | Add matching public property |
| PRAG0505 | Endpoints | Error | Duplicate endpoint name | Use unique endpoint names |
| PRAG0506 | Endpoints | Error | Invalid rate limit config | Specify policy or Requests+Window |
| PRAG0507 | Endpoints | Error | Endpoint group not found | Create [EndpointGroup] class |
| PRAG0508 | Endpoints | Error | Processor must implement interface | Implement pre/post processor interface |
| PRAG0510 | Endpoints | Info | Endpoint registered | Informational |
| PRAG0511 | Endpoints | Info | DomainAction endpoint detected | Informational |
| PRAG0512 | Endpoints | Info | Implicit body binding | Add [FromBody]/[FromQuery]/[FromRoute] |
| PRAG0515 | Endpoints | Error | Autocomplete missing key | Add Id or [Key] property to entity |
| PRAG0550 | Endpoints | Error | Autocomplete requires string | Apply to string properties only |
| PRAG0551 | Endpoints | Warning | Versioning requires Asp.Versioning.Http | Add NuGet package reference |
| PRAG0600 | Persistence | Error | Type must be partial | Add partial keyword |
| PRAG0601 | Persistence | Error | Entity type not found | Check type reference exists |
| PRAG0602 | Persistence | Error | Database context must be partial | Add partial keyword |
| PRAG0603 | Persistence | Error | ReadAccess cross-database | Use same database or remove ReadAccess |
| PRAG0604 | Persistence | Error | ReadAccess owner not included | Add module reference |
| PRAG0610 | Persistence | Error | Collection without Relation attribute | Add [Relation.OneToMany<T>] |
| PRAG0611 | Persistence | Error | Reference nav without Relation | Add [Relation.ManyToOne<T>] |
| PRAG0612 | Persistence | Error | Ambiguous relation needs WithNavigation | Add .WithNavigation("Name") |
| PRAG0613 | Persistence | Error | Inverse property not found | Check inverse name matches |
| PRAG0614 | Persistence | Error | Inverse type mismatch | Fix relation pairing types |
| PRAG0615 | Persistence | Error | Duplicate navigation name | Use unique navigation names |
| PRAG0616 | Persistence | Warning | Prefer OneToMany on parent | Move relation to parent side |
| PRAG0617 | Persistence | Info | Cross-boundary relation detected | Informational — FK only, no nav |
| PRAG0620 | Persistence | Warning | State machine missing initial state | Add [InitialState] to one value |
| PRAG0621 | Persistence | Warning | Unreachable state | Add [TransitionFrom] or [InitialState] |
| PRAG0622 | Persistence | Error | Invalid transition source | Use valid enum member name |
| PRAG0650 | Persistence | Warning | Navigation without foreign key | Add FK property or configure manually |
| PRAG0651 | Persistence | Warning | Property may need value converter | Register a ValueConverter |
| PRAG0705 | Query | Warning | Required nav to soft-deletable entity | Make navigation optional |
| PRAG0710 | Query | Warning | DTO nav without Include | Check property name matches entity nav |
| PRAG0711 | Query | Warning | Deep Include depth on loading profile | Reduce MaxDepth or use Projection |
| PRAG0716 | Query | Warning | DTO with many navigation levels | Use Projection strategy |
| PRAG0800 | Messaging | Error | Handler must implement IMessageHandler<T> | Add interface implementation |
| PRAG0801 | Messaging | Warning | Handler should be partial | Add partial keyword |
| PRAG0802 | Messaging | Error | Invalid retry config | Set MaxAttempts > 0 |
| PRAG0803 | Messaging | Error | Middleware must implement IMessageMiddleware | Add interface implementation |
| PRAG0810 | Messaging | Error | Inconsistent saga transition | Verify state graph |
| PRAG0811 | Messaging | Info | Saga state has no handler | Add handler or confirm terminal |
| PRAG0812 | Messaging | Warning | Saga has unreachable states | Add transitions to reach all states |
| PRAG0813 | Messaging | Error | Saga state must be enum | Change type parameter to enum |
| PRAG0814 | Messaging | Error | Saga missing start handler | Add [SagaStart] to one method |
| PRAG0815 | Messaging | Warning | Cross-boundary without DependsOn | Add [DependsOn<TModule>] |
| PRAG0816 | Messaging | Warning | Event without consumers | Add handler or remove event |
| PRAG0817 | Messaging | Error | Queue name collision | Rename one handler |
| PRAG0818 | Messaging | Warning | Cross-boundary without outbox | Add [EnableOutbox] to DbContext |
| PRAG0830 | Messaging | Error | EnableOutbox requires DbContext | Move attribute to DbContext class |
| PRAG0831 | Messaging | Error | EnableOutbox requires EFCore package | Add Pragmatic.Messaging.EFCore ref |
| PRAG1000 | Identity | Error | IPermission.Name must be non-empty | Return non-empty string from Name |
| PRAG1001 | Identity | Error | Duplicate permission name | Use unique permission names |
| PRAG1002 | Identity | Warning | Permission name convention | Use boundary.sub.verb format |
| PRAG1003 | Identity | Error | IRole.Name must be non-empty | Return non-empty string from Name |
| PRAG1050 | Composition | Error | Duplicate UsePackage | Remove duplicate declaration |
| PRAG1100 | Ownership | Error | OwnedEntity requires partial | Add partial keyword |
| PRAG1102 | Ownership | Error | OwnedEntity requires Entity<T> | Add [Entity<TKey>] attribute |
| PRAG1104 | Ownership | Info | OwnerId manually declared | Informational — SG skips generation |
| PRAG1600 | Composition | Error | Module requires unavailable middleware | Add required middleware package |
| PRAG1601 | Composition | Error | Module dependency not declared | Declare missing dependency |
| PRAG1602 | Composition | Error | Circular dependency | Remove circular module reference |
| PRAG1605 | Composition | Warning | Service without Composition ref | Add Pragmatic.Composition reference |
| PRAG1606 | Composition | Error | Decorator requires Composition | Add Pragmatic.Composition reference |
| PRAG1610 | Composition | Error | Incompatible schema version | Update packages to same major version |
| PRAG1611 | Composition | Warning | Newer schema version | Update Pragmatic.Composition package |
| PRAG1612 | Composition | Info | Legacy schema version | Informational — compat mode |
| PRAG1620 | Composition | Error | Inject references unregistered service | Register the service with [Service] |
| PRAG1621 | Composition | Error | Inject creates circular reference | Refactor to break cycle |
| PRAG1630 | Composition | Error | StartupStep must implement IStartupStep | Implement the interface |
| PRAG1631 | Composition | Error | StartupStep must be on a class | Move attribute to class |
| PRAG1632 | Composition | Error | NeedsStep references unavailable type | Add required package reference |
| PRAG1640 | Composition | Error | Service requires class | Move [Service] to a class |
| PRAG1641 | Composition | Warning | Dependency not registered | Register or add [Service] |
| PRAG1642 | Composition | Warning | Lifetime mismatch (captive dependency) | Align lifetimes |
| PRAG1643 | Composition | Warning | No interface found | Use AsSelf=true or add interface |
| PRAG1644 | Composition | Info | Service registered | Informational |
| PRAG1645 | Composition | Error | Abstract class cannot be service | Remove abstract or [Service] |
| PRAG1646 | Composition | Warning | Keyed services require .NET 8+ | Upgrade to .NET 8+ or remove Key |
| PRAG1651 | Composition | Warning | Boundary without database | Use [Include<T, TDb>] |
| PRAG1652 | Composition | Error | DbContext name collision | Use distinct DbContext types |
| PRAG1660 | Composition | Error | Decorator must implement interface | Implement at least one interface |
| PRAG1661 | Composition | Error | Decorator missing inner service | Add ctor parameter of decorated type |
| PRAG1670 | Composition | Error | EventHandler missing interface | Implement IDomainEventHandler<T> |
| PRAG1680 | Composition | Warning | ExposeEndpoint not from package | Use [Endpoint] directly |
| PRAG1685 | Composition | Error | RemoteBoundary overlaps Include | Remove one declaration |
| PRAG1686 | Composition | Warning | RemoteBoundary has no actions | Add actions or remove attribute |
| PRAG1687 | Composition | Info | RemoteBoundary base URL not configured | Configure at runtime |
| PRAG1688 | Composition | Warning | Config key missing from appsettings | Add key to appsettings.json |
| PRAG1690 | Composition | Info | Discovered Pragmatic modules | Informational |
| PRAG1691 | Composition | Info | Module composition info | Informational |
| PRAG1693 | Composition | Info | No services discovered | Check [Service] usage |
| PRAG1694 | Composition | Info | No startup steps discovered | Informational |
| PRAG1695 | Composition | Warning | Authorization without Identity | Add Pragmatic.Identity reference |
| PRAG1696 | Composition | Warning | Identity.Persistence without Authorization | Add Pragmatic.Authorization reference |
| PRAG1700 | Caching | Error | Type must be partial | Add partial keyword |
| PRAG1701 | Caching | Error | Invalid cache duration | Use 5m, 1h, 1d format |
| PRAG1702 | Caching | Error | No cache key properties | Add at least one public property |
| PRAG1703 | Caching | Error | Invalid placeholder in tag/key | Check placeholder name matches property |
| PRAG1750 | Caching | Warning | Duplicate cache key order | Assign unique Order values |
| PRAG1751 | Caching | Warning | All properties excluded | Keep at least one property |
| PRAG1800 | I18n | Error | Invalid translation file | Fix JSON syntax |
| PRAG1801 | I18n | Warning | Duplicate translation key | Consolidate into single file |
| PRAG1802 | I18n | Warning | Missing translation key | Add key to target culture |
| PRAG1803 | I18n | Info | Empty translation file | Add key-value pairs |
| PRAG1900 | Patch | Error | Patch type must be partial | Add partial keyword |
| PRAG1901 | Patch | Error | Entity type not found | Check type reference |
| PRAG1902 | Patch | Warning | No patchable properties | Add settable properties |
| PRAG2000 | Configuration | Error | Configuration class must be partial | Add partial keyword |
| PRAG2001 | Configuration | Error | Cannot be static or abstract | Remove modifier |
| PRAG2050 | Configuration | Warning | Required property has default | Reconsider [Required] usage |
| PRAG2500 | Jobs | Error | Must implement IJob or IJob<T> | Add interface |
| PRAG2501 | Jobs | Error | Invalid cron expression | Provide valid cron string |
| PRAG2502 | Jobs | Warning | Job should be partial | Add partial keyword |
| PRAG2503 | Jobs | Error | Duplicate recurring job ID | Use unique ID |
| PRAG2504 | Jobs | Error | Invalid retry MaxAttempts | Set MaxAttempts > 0 |
| PRAG2505 | Jobs | Error | Continuation must be a job | Implement IJob on continuation type |
| PRAG2506 | Jobs | Error | Continuation cycle detected | Remove cyclic [ContinueWith] |
Roslyn Analyzer Diagnostics (Pragmatic.Persistence.Analyzers)
Section titled “Roslyn Analyzer Diagnostics (Pragmatic.Persistence.Analyzers)”| ID | Module | Severity | Title | Quick Fix |
|---|---|---|---|---|
| PRAG0680 | Persistence | Warning | Use Entity.Create() instead of new | Replace new Entity() with Entity.Create() |
| PRAG0681 | Persistence | Warning | Do not use default for entities | Use Entity.Create() factory |
| PRAG0682 | Persistence | Warning | Do not use Activator.CreateInstance | Use Entity.Create() factory |
3. Per-Module Diagnostic Details
Section titled “3. Per-Module Diagnostic Details”3.1 Validation (PRAG0200—PRAG0209)
Section titled “3.1 Validation (PRAG0200—PRAG0209)”PRAG0200 — Type with validation attributes must be partial
Section titled “PRAG0200 — Type with validation attributes must be partial”What it means: You applied validation attributes (e.g., [Required], [MinLength]) to properties
on a class that is not declared as partial.
Why it triggers: The SG needs to add a Validate() method to the class, which requires partial.
How to fix:
// Beforepublic class CreateUserRequest{ [Required] public string Name { get; set; }}
// Afterpublic partial class CreateUserRequest{ [Required] public string Name { get; set; }}PRAG0201 — Validator must implement IValidator<T>
Section titled “PRAG0201 — Validator must implement IValidator<T>”What it means: A class marked with [Validator] does not implement IValidator<T>.
How to fix:
[Validator]public class UserValidator : IValidator<CreateUserRequest>{ public ValidationResult Validate(CreateUserRequest instance) { /* ... */ }}PRAG0203 — Comparison property not found
Section titled “PRAG0203 — Comparison property not found”What it means: An attribute like [EqualTo("OtherProp")] or [GreaterThanProperty("OtherProp")]
references a property that does not exist on the same type.
How to fix: Check spelling and ensure both properties are on the same type.
PRAG0205 — ValidateElements element type not validatable
Section titled “PRAG0205 — ValidateElements element type not validatable”What it means: [ValidateElements] was used on a collection whose element type does not have
validation attributes (i.e., does not implement ISyncValidator).
How to fix: Add validation attributes to the element type and make it partial.
3.2 Mapping (PRAG0300—PRAG0325)
Section titled “3.2 Mapping (PRAG0300—PRAG0325)”PRAG0300 — Type must be partial
Section titled “PRAG0300 — Type must be partial”What it means: A class with [MapFrom<T>] or [MapTo<T>] is not declared as partial.
How to fix: Add the partial keyword.
PRAG0303 — No matching source property
Section titled “PRAG0303 — No matching source property”What it means: A target DTO property has no corresponding property on the source type (by name convention).
How to fix:
// Option A: map explicitly[MapProperty(From = nameof(User.FullName))]public string DisplayName { get; set; }
// Option B: ignore explicitly[MapIgnore]public string ComputedField { get; set; }When to suppress: If you intentionally set the property manually after mapping.
PRAG0304 — Incompatible types
Section titled “PRAG0304 — Incompatible types”What it means: The SG cannot auto-convert between source and target property types
(e.g., int to string).
How to fix: Add a [MapConverter<TConverter>] attribute or change one of the types.
PRAG0307 — Required property not mapped
Section titled “PRAG0307 — Required property not mapped”What it means: A target property is marked required but has no source mapping.
How to fix: Either map the property explicitly or remove the required modifier.
PRAG0310 — GenerateProjection requires MapFrom
Section titled “PRAG0310 — GenerateProjection requires MapFrom”What it means: [GenerateProjection] was applied without a corresponding [MapFrom<T>].
Projection generation needs to know the source entity.
How to fix:
[MapFrom<User>][GenerateProjection]public partial class UserDto { /* ... */ }PRAG0314 — Conflicting attributes
Section titled “PRAG0314 — Conflicting attributes”What it means: A property has both [MapIgnore] and [MapProperty] which contradict each other.
How to fix: Remove one of the two attributes.
PRAG0317 — Nullable to non-nullable without Default
Section titled “PRAG0317 — Nullable to non-nullable without Default”What it means: A nullable source property maps to a non-nullable target without specifying a default.
How to fix:
[MapProperty(Default = "\"\"")] // default to empty stringpublic string Name { get; set; }PRAG0325 — Source property not mapped to DTO
Section titled “PRAG0325 — Source property not mapped to DTO”What it means: A source entity property is not represented in the target DTO. This is a Hidden diagnostic, useful for catching accidental omissions.
How to configure: Promote to Warning in .editorconfig:
dotnet_diagnostic.PRAG0325.severity = warning3.3 Actions (PRAG0400—PRAG0413)
Section titled “3.3 Actions (PRAG0400—PRAG0413)”PRAG0400 — Action class must be partial
Section titled “PRAG0400 — Action class must be partial”What it means: A class using action attributes (e.g., [DomainAction], [Mutation]) must be partial
so the SG can generate the invoker.
PRAG0401 — Must inherit from DomainAction base
Section titled “PRAG0401 — Must inherit from DomainAction base”What it means: The action class does not inherit from DomainAction<T> or VoidDomainAction.
How to fix:
public partial class GetUser : DomainAction<UserDto>{ public Guid UserId { get; set; } // ...}PRAG0404 — LoadEntity ID property not found
Section titled “PRAG0404 — LoadEntity ID property not found”What it means: [LoadEntity<TEntity>] specifies (or defaults to) an ID property name that
does not exist on the action class.
How to fix: Add the ID property or specify the correct property name:
[LoadEntity<Reservation>(IdProperty = "ReservationId")]public partial class UpdateReservation : Mutation<Reservation>{ public Guid ReservationId { get; set; }}PRAG0409 — Mutation must inherit from Mutation<TEntity>
Section titled “PRAG0409 — Mutation must inherit from Mutation<TEntity>”What it means: A class decorated with [Mutation] does not inherit from Mutation<TEntity>.
PRAG0410 — Mutation mode not determined
Section titled “PRAG0410 — Mutation mode not determined”What it means: The SG cannot determine whether this is a Create or Update mutation.
How to fix:
// Option A: use a class name prefixpublic partial class CreateReservation : Mutation<Reservation> { }public partial class UpdateReservation : Mutation<Reservation> { }
// Option B: specify explicitly[Mutation(Mode = MutationMode.Create)]public partial class NewBooking : Mutation<Reservation> { }PRAG0412 — SubBoundary nesting deeper than 2 levels
Section titled “PRAG0412 — SubBoundary nesting deeper than 2 levels”What it means: The SG inferred a sub-boundary from namespace structure that is more than 2 levels deep. Deep nesting creates overly complex generated interfaces.
When to suppress: If the nesting is intentional and well-understood.
3.4 Endpoints (PRAG0500—PRAG0551)
Section titled “3.4 Endpoints (PRAG0500—PRAG0551)”PRAG0500 — Endpoint class must be partial
Section titled “PRAG0500 — Endpoint class must be partial”What it means: A class with [Endpoint] must be partial.
PRAG0502 — Route is required
Section titled “PRAG0502 — Route is required”What it means: The [Endpoint] attribute must include a route pattern.
How to fix:
[Endpoint(HttpVerb.Get, "/api/users/{UserId}")]public partial class GetUserEndpoint : Endpoint<UserDto> { }PRAG0504 — Route parameter not found
Section titled “PRAG0504 — Route parameter not found”What it means: A {parameter} in the route does not match any public property on the endpoint.
How to fix: Add a matching property:
[Endpoint(HttpVerb.Get, "/api/users/{UserId}")]public partial class GetUserEndpoint : Endpoint<UserDto>{ public Guid UserId { get; set; } // matches {UserId}}PRAG0505 — Duplicate endpoint name
Section titled “PRAG0505 — Duplicate endpoint name”What it means: Two endpoints resolve to the same name, which breaks link generation.
How to fix: Rename one of the endpoint classes or specify a custom name in the attribute.
PRAG0515 / PRAG0550 — Autocomplete diagnostics
Section titled “PRAG0515 / PRAG0550 — Autocomplete diagnostics”What they mean: [Autocomplete] requires the entity to have an identifiable key property,
and the attribute must be placed on a string property.
How to fix:
[Entity<Guid>]public partial class Amenity{ [Autocomplete] // must be string public string Name { get; set; }}PRAG0551 — Versioned methods require Asp.Versioning.Http
Section titled “PRAG0551 — Versioned methods require Asp.Versioning.Http”What it means: Your endpoint has versioned methods (ExecuteV2, HandleAsyncV2, etc.) but the
Asp.Versioning.Http NuGet package is not referenced. Without it, only the default version is generated.
How to fix: Add the NuGet package:
<PackageReference Include="Asp.Versioning.Http" Version="..." />3.5 Persistence (PRAG0600—PRAG0651)
Section titled “3.5 Persistence (PRAG0600—PRAG0651)”PRAG0600 — Type must be partial
Section titled “PRAG0600 — Type must be partial”What it means: An entity or persistence-related type is not partial.
PRAG0603 / PRAG0604 — ReadAccess diagnostics
Section titled “PRAG0603 / PRAG0604 — ReadAccess diagnostics”What they mean: [ReadAccess<T>] allows a boundary to read another boundary’s entity.
PRAG0603 fires when the two boundaries use different databases (cross-DB join is impossible).
PRAG0604 fires when the owner module is not referenced.
PRAG0610—PRAG0615 — Relation attribute diagnostics
Section titled “PRAG0610—PRAG0615 — Relation attribute diagnostics”These diagnostics enforce proper use of [Relation.*] attributes.
PRAG0610 — Collection without Relation attribute:
// Before -- implicit collection triggers PRAG0610[Entity<Guid>]public partial class Order{ public List<OrderItem> Items { get; set; } // PRAG0610}
// After -- explicit relation[Entity<Guid>][Relation.OneToMany<OrderItem>]public partial class Order { }PRAG0612 — Ambiguous relation:
// Two relations to the same target -- use WithNavigation to disambiguate[Relation.OneToMany<Address>(WithNavigation = "HomeAddress")][Relation.OneToMany<Address>(WithNavigation = "WorkAddress")]public partial class Person { }PRAG0620—PRAG0622 — State Machine diagnostics
Section titled “PRAG0620—PRAG0622 — State Machine diagnostics”PRAG0620 — Missing initial state:
public enum OrderStatus{ [InitialState] // add this to fix PRAG0620 Draft,
[TransitionFrom(nameof(Draft))] Confirmed,
[TransitionFrom(nameof(Confirmed))] Completed}PRAG0622 — Invalid transition source: Ensure all [TransitionFrom] values match actual enum members.
3.6 Query Pipeline (PRAG0705—PRAG0716)
Section titled “3.6 Query Pipeline (PRAG0705—PRAG0716)”PRAG0705 — Required navigation to soft-deletable entity
Section titled “PRAG0705 — Required navigation to soft-deletable entity”What it means: A required navigation to a soft-deletable entity causes EF Core to INNER JOIN, which makes child rows disappear when the parent is soft-deleted.
How to fix: Make the navigation optional:
// Before[Relation.ManyToOne<Organization>(Required = true)]
// After[Relation.ManyToOne<Organization>(Required = false)]PRAG0711 — Deep Include depth
Section titled “PRAG0711 — Deep Include depth”What it means: A [LoadWith] profile with MaxDepth > 3 will generate complex SQL.
How to fix: Reduce depth or switch to Projection strategy:
[LoadWith<Order>(MaxDepth = 2)] // reduce depth[LoadWith<Order>(Strategy = QueryStrategy.Projection)] // or use projection3.7 Persistence Analyzers (PRAG0680—PRAG0682)
Section titled “3.7 Persistence Analyzers (PRAG0680—PRAG0682)”These are Roslyn analyzers (not SG diagnostics) in Pragmatic.Persistence.Analyzers.
They run on your application code to enforce the zero-reflection principle.
PRAG0680 — Use Entity.Create() instead of new
Section titled “PRAG0680 — Use Entity.Create() instead of new”What it means: You wrote new Entity() instead of using the generated factory.
Why it matters: Entity.Create() ensures Guid v7 ID generation, audit timestamps, and default values.
How to fix:
// Beforevar reservation = new Reservation();
// Aftervar reservation = Reservation.Create();When to suppress: Never in application code. The analyzer already excludes generated code.
PRAG0681 — Do not use default for entities
Section titled “PRAG0681 — Do not use default for entities”What it means: default(Entity) or default assigned to an entity variable bypasses initialization.
How to fix: Use Entity.Create() instead.
PRAG0682 — Do not use Activator.CreateInstance
Section titled “PRAG0682 — Do not use Activator.CreateInstance”What it means: Activator.CreateInstance<Entity>() or Activator.CreateInstance(typeof(Entity))
creates an entity via reflection, bypassing the factory.
How to fix: Use Entity.Create(). Pragmatic follows a zero-reflection principle.
3.8 Messaging (PRAG0800—PRAG0831)
Section titled “3.8 Messaging (PRAG0800—PRAG0831)”PRAG0800 — Handler must implement IMessageHandler<T>
Section titled “PRAG0800 — Handler must implement IMessageHandler<T>”How to fix:
[MessageHandler]public partial class InvoicePaidHandler : IMessageHandler<InvoicePaid>{ public Task HandleAsync(InvoicePaid message, MessageContext ctx, CancellationToken ct) { // ... }}PRAG0802 — Invalid retry configuration
Section titled “PRAG0802 — Invalid retry configuration”How to fix:
[MessageHandler][Retry(MaxAttempts = 3, DelaySeconds = 5)] // MaxAttempts must be > 0public partial class MyHandler : IMessageHandler<MyEvent> { }PRAG0810—PRAG0814 — Saga diagnostics
Section titled “PRAG0810—PRAG0814 — Saga diagnostics”These validate the state graph of [Saga<TState>] classes.
PRAG0814 — Saga has no start handler:
[Saga<BookingState>]public partial class BookingSaga{ [SagaStart] // required -- exactly one method public Task HandleStart(BookingRequested msg) { /* ... */ }
[InState(BookingState.PaymentPending)] public Task HandlePayment(PaymentReceived msg) { /* ... */ }}PRAG0812 — Unreachable states: Every non-initial state must be reachable via at least one
transition from the start state. Check your [InState] and state assignment logic.
PRAG0830 / PRAG0831 — Outbox diagnostics
Section titled “PRAG0830 / PRAG0831 — Outbox diagnostics”PRAG0830: [EnableOutbox] can only be applied to a class inheriting from DbContext.
PRAG0831: [EnableOutbox] requires a reference to Pragmatic.Messaging.EFCore:
<PackageReference Include="Pragmatic.Messaging.EFCore" />3.9 Identity / Authorization (PRAG1000—PRAG1003)
Section titled “3.9 Identity / Authorization (PRAG1000—PRAG1003)”PRAG1000 — IPermission.Name must be non-empty
Section titled “PRAG1000 — IPermission.Name must be non-empty”How to fix:
public sealed class ViewReservations : IPermission{ public static string Name => "booking.reservations.view"; // non-empty}PRAG1001 — Duplicate permission name
Section titled “PRAG1001 — Duplicate permission name”What it means: Two IPermission implementations return the same Name string.
How to fix: Ensure each permission has a globally unique name.
PRAG1002 — Permission name convention
Section titled “PRAG1002 — Permission name convention”What it means: The recommended format is boundary.sub-boundary.verb.
When to suppress: If your naming convention intentionally differs.
3.10 Data Ownership (PRAG1100—PRAG1104)
Section titled “3.10 Data Ownership (PRAG1100—PRAG1104)”PRAG1100 — OwnedEntity requires partial
Section titled “PRAG1100 — OwnedEntity requires partial”How to fix: Add partial to the class declaration.
PRAG1102 — OwnedEntity requires Entity<T>
Section titled “PRAG1102 — OwnedEntity requires Entity<T>”What it means: [OwnedEntity] only works on classes that also have [Entity<TKey>].
How to fix:
[Entity<Guid>][OwnedEntity]public partial class Reservation { /* ... */ }3.11 Composition (PRAG1050, PRAG1600—PRAG1696)
Section titled “3.11 Composition (PRAG1050, PRAG1600—PRAG1696)”PRAG1602 — Circular dependency
Section titled “PRAG1602 — Circular dependency”What it means: Module A depends on Module B, which depends on Module A (directly or transitively).
How to fix: Refactor to break the cycle. Introduce a shared abstraction module if needed.
PRAG1605 — Service without Composition reference
Section titled “PRAG1605 — Service without Composition reference”What it means: [Service] was used in a project that does not reference Pragmatic.Composition.
The service will not be auto-discovered.
How to fix: Add a project/package reference to Pragmatic.Composition.
PRAG1642 — Lifetime mismatch (captive dependency)
Section titled “PRAG1642 — Lifetime mismatch (captive dependency)”What it means: A Singleton service depends on a Scoped or Transient service, which causes the shorter-lived service to be “captured” and held alive longer than intended.
How to fix:
// Option A: make the dependent service Singleton too (if stateless)[Service(Lifetime = ServiceLifetime.Singleton)]public class MyCacheService : ICacheService { }
// Option B: inject IServiceScopeFactory and resolve per-requestPRAG1652 — DbContext name collision
Section titled “PRAG1652 — DbContext name collision”What it means: Two different [Database] declarations generate DbContext classes with the same name.
How to fix: Use [Include<TModule, TDatabase, TDbContext>] with distinct DbContext type parameters.
PRAG1685 — RemoteBoundary overlaps Include
Section titled “PRAG1685 — RemoteBoundary overlaps Include”What it means: A module is declared as both [Include<T>] (local) and [RemoteBoundary<T>] (remote).
A boundary must be one or the other.
PRAG1695 / PRAG1696 — Module dependency chain warnings
Section titled “PRAG1695 / PRAG1696 — Module dependency chain warnings”These warn about incomplete authorization/identity package chains. If you reference
Pragmatic.Authorization, you typically also need Pragmatic.Identity, and vice versa
for Identity.Persistence.
3.12 Caching (PRAG1700—PRAG1751)
Section titled “3.12 Caching (PRAG1700—PRAG1751)”PRAG1701 — Invalid cache duration
Section titled “PRAG1701 — Invalid cache duration”How to fix:
[Cacheable(Duration = "5m")] // 5 minutes[Cacheable(Duration = "1h")] // 1 hour[Cacheable(Duration = "1d")] // 1 day[Cacheable(Duration = "00:30:00")] // TimeSpan formatPRAG1703 — Invalid placeholder
Section titled “PRAG1703 — Invalid placeholder”What it means: A {PropertyName} placeholder in the cache key/tag template references a property
that does not exist on the type.
How to fix: Verify the placeholder matches an existing public property name.
3.13 Internationalization (PRAG1800—PRAG1803)
Section titled “3.13 Internationalization (PRAG1800—PRAG1803)”PRAG1800 — Invalid translation file
Section titled “PRAG1800 — Invalid translation file”What it means: A *.json translation file has syntax errors.
How to fix: Validate the JSON file. Common issues: trailing commas, unquoted keys, BOM encoding.
PRAG1802 — Missing translation key
Section titled “PRAG1802 — Missing translation key”What it means: A key exists in the default culture (e.g., en.json) but is missing in another
culture (e.g., it.json). At runtime, the default culture value will be used as fallback.
How to fix: Add the missing key to the target culture file.
3.14 Patch (PRAG1900—PRAG1902)
Section titled “3.14 Patch (PRAG1900—PRAG1902)”PRAG1900 — Patch type must be partial
Section titled “PRAG1900 — Patch type must be partial”How to fix:
[GeneratePatch<Reservation>]public partial class ReservationPatch { }PRAG1902 — No patchable properties
Section titled “PRAG1902 — No patchable properties”What it means: The target entity has no settable properties for patching.
When to suppress: If the entity intentionally uses only init-only or private-set properties.
3.15 Configuration (PRAG2000—PRAG2050)
Section titled “3.15 Configuration (PRAG2000—PRAG2050)”PRAG2000 — Configuration class must be partial
Section titled “PRAG2000 — Configuration class must be partial”How to fix: Add partial to the class with [Configuration].
PRAG2050 — Required property has default value
Section titled “PRAG2050 — Required property has default value”What it means: A property marked [Required] also has a default value. The default satisfies
the requirement, making [Required] effectively meaningless.
When to suppress: If you want to enforce explicit configuration even when a default exists.
3.16 Jobs (PRAG2500—PRAG2506)
Section titled “3.16 Jobs (PRAG2500—PRAG2506)”PRAG2500 — Must implement IJob or IJob<T>
Section titled “PRAG2500 — Must implement IJob or IJob<T>”How to fix:
[Job]public partial class CleanupJob : IJob{ public Task ExecuteAsync(CancellationToken ct) { /* ... */ }}PRAG2501 — Invalid cron expression
Section titled “PRAG2501 — Invalid cron expression”How to fix:
[RecurringJob("0 2 * * *")] // daily at 2 AM -- valid cronpublic partial class NightlyCleanup : IJob { }PRAG2503 — Duplicate recurring job ID
Section titled “PRAG2503 — Duplicate recurring job ID”What it means: Two [RecurringJob] classes resolve to the same ID (auto-generated from class name
or explicitly set).
How to fix:
[RecurringJob("0 2 * * *", Id = "cleanup-orders")]public partial class OrderCleanup : IJob { }
[RecurringJob("0 3 * * *", Id = "cleanup-logs")]public partial class LogCleanup : IJob { }PRAG2505 / PRAG2506 — Continuation diagnostics
Section titled “PRAG2505 / PRAG2506 — Continuation diagnostics”PRAG2505: The type referenced by [ContinueWith<T>] must implement IJob or IJob<T>.
PRAG2506: A cycle in the continuation chain was detected (e.g., A continues with B, B continues with A).
Remove one [ContinueWith] to break the cycle.
4. Common Troubleshooting Patterns
Section titled “4. Common Troubleshooting Patterns”4.1 “My SG Is Not Generating Code”
Section titled “4.1 “My SG Is Not Generating Code””Use this decision tree when you expect generated code but nothing appears:
-
Is the class
partial? Almost every Pragmatic attribute requires the target class to bepartial. Check for PRAG0200/0300/0400/0500/0600/1700/1900/2000/2500. -
Does the class inherit from the correct base?
- Actions:
DomainAction<T>orVoidDomainAction - Mutations:
Mutation<TEntity> - Endpoints:
Endpoint<T>orVoidEndpoint
- Actions:
-
Are the NuGet references correct? Run
dotnet buildwith verbosity set todiagnosticto see which SG pipelines are active:Terminal window dotnet build -v diag 2>&1 | grep "Pragmatic" -
Is FeatureDetector finding the module? Check for PRAG1690/1691 Info diagnostics. If no modules are discovered, ensure your library project references the correct Pragmatic runtime package.
-
Clean and rebuild. SG output is cached aggressively. Force a clean rebuild:
Terminal window dotnet clean && dotnet build -
Check for SG errors in build output. The SG may silently skip generation when it encounters an error. Look for
PRAGentries in the Warning/Error list.
4.2 “I Get a PRAG Error but My Code Looks Correct”
Section titled “4.2 “I Get a PRAG Error but My Code Looks Correct””Checklist:
- Rebuild the solution. Stale SG state can report phantom errors.
- Check namespace. Attributes must be from Pragmatic namespaces, not similarly named third-party ones.
- Check using directives. Ensure you are importing the correct namespace for the attribute.
- Check target framework. Some features require specific .NET versions (e.g., PRAG1646 for keyed services).
- Check attribute spelling. Generic attributes (
[Entity<Guid>]) vs non-generic ([Entity(typeof(Guid))]). Pragmatic exclusively uses generic attributes. - Verify package versions match. Mixed versions across Pragmatic packages can cause schema version diagnostics (PRAG1610/1611/1612).
4.3 “How to Add a New Diagnostic”
Section titled “4.3 “How to Add a New Diagnostic””For contributors adding diagnostics to the Pragmatic source generator:
- Determine the module and allocate an ID from the correct range (see
shared/SourceGen/DiagnosticDescriptors.cs). - Create or extend a
*Diagnostics.csfile in the feature’sDiagnostics/folder. - Use
DiagnosticFactory.Error/Warning/Info()fromshared/SourceGen/:public static readonly DiagnosticDescriptor MyNewRule = DiagnosticFactory.Error("PRAG0XXX","Short title","Message with '{0}' placeholder for context","Longer description with fix guidance."); - Report the diagnostic in the Transform or Feature using
context.ReportDiagnostic(descriptor, location, args). - Add a test in the module’s test project that verifies the diagnostic is emitted:
[Fact]public void MyRule_InvalidInput_EmitsDiagnostic(){var source = "/* invalid code triggering the rule */";var result = RunGenerator(source);HasDiagnostic(result, "PRAG0XXX").Should().BeTrue();}
- Update this troubleshooting guide with the new diagnostic entry.
Appendix: ID Range Allocation
Section titled “Appendix: ID Range Allocation”| Range | Module | File |
|---|---|---|
| PRAG0200—0299 | Validation | ValidationDiagnostics.cs |
| PRAG0300—0399 | Mapping | MappingDiagnostics.cs |
| PRAG0400—0449 | Actions | ActionsDiagnostics.cs |
| PRAG0500—0599 | Endpoints | EndpointsDiagnostics.cs |
| PRAG0600—0699 | Persistence + EF Core | PersistenceDiagnostics.cs |
| PRAG0680—0682 | Persistence Analyzers | DiagnosticDescriptors.cs (Analyzers) |
| PRAG0700—0716 | Query Pipeline | QueryPipelineDiagnostics.cs |
| PRAG0800—0831 | Messaging | MessagingDiagnostics.cs |
| PRAG1000—1099 | Identity / Authorization | IdentityDiagnostics.cs |
| PRAG1050—1059 | Package Composition | CompositionDiagnostics.cs |
| PRAG1100—1109 | Data Ownership | OwnershipDiagnostics.cs |
| PRAG1600—1699 | Composition | CompositionDiagnostics.cs |
| PRAG1700—1799 | Caching | CachingDiagnostics.cs |
| PRAG1800—1899 | Internationalization | I18NDiagnostics.cs |
| PRAG1900—1999 | Patch | PatchDiagnostics.cs |
| PRAG2000—2099 | Configuration | ConfigurationDiagnostics.cs |
| PRAG2500—2549 | Jobs | JobsDiagnostics.cs |