Build Diagnostics
Reference for SPIDERLY### diagnostic codes emitted by Spiderly source generators when entity definitions violate a contract.
Overview
When the build emits hundreds of CS0246 errors about missing {Entity}DTO types, look for the SPIDERLY-prefixed diagnostic first — that is the real cause. A single contract violation aborts MapperGenerator, which deletes every entity's generated DTO and produces a flood of downstream CS0246 errors that look unrelated.
When an entity class violates a contract the source generators rely on — a missing base class, a malformed [ForeignKey], an ambiguous relationship, a broken [DisplayName] path — Spiderly emits a located Roslyn diagnostic at build time rather than crashing with CS8785 "generator failed to generate source".
Each diagnostic:
- Has a stable
SPIDERLY###ID (never reused, never renumbered). - Renders as a red squiggle in Visual Studio / Rider / VS Code on the offending class or property.
- Appears in the build output with a clickable file and line.
- Is parseable by CI logs and AI agents for automated fix suggestions.
Diagnostics are reported by the generator's SourceProductionContext, so they behave like any other compiler warning or error — you can suppress individual codes via <NoWarn> in the .csproj, treat warnings as errors, or filter them in CI output. Severity is Error for everything except SPIDERLY013 (Warning).
Diagnostic Reference
| ID | Severity | Title |
|---|---|---|
| SPIDERLY001 | Error | Controller type is not a discovered entity or DTO |
| SPIDERLY002 | Error | Many-to-many entity requires exactly two [M2MWithMany] properties |
| SPIDERLY003 | Error | [ForeignKey] references a property that does not exist |
| SPIDERLY004 | Error | Foreign key type does not match target primary key |
| SPIDERLY005 | Error | Foreign key is ambiguous — multiple convention matches |
| SPIDERLY006 | Error | Foreign key nullability does not match navigation property |
| SPIDERLY007 | Error | [DisplayName] path references a property that does not exist |
| SPIDERLY008 | Error | [DisplayName] path segment is not a many-to-one navigation |
| SPIDERLY009 | Error | [DisplayName] navigation target entity not found |
| SPIDERLY010 | Error | Entity missing required BusinessObject<T> / ReadonlyObject<T> base |
| SPIDERLY011 | Error | Controller property type is not resolvable for client generation |
| SPIDERLY012 | Error | One-to-many back-reference missing [M2MWithMany] |
| SPIDERLY013 | Warning | Backend folder not found under calling project |
| SPIDERLY014 | Error | Blob property missing [AcceptedFileTypes] attribute |
| SPIDERLY015 | Error | Many-to-one navigation missing [WithMany] attribute |
| SPIDERLY016 | Error | [WithMany] target collection does not exist on the related entity |
| SPIDERLY017 | Error | [WithMany] target collection has the wrong element type |
| SPIDERLY018 | Error | Entity primary key type must be int, long, or byte |
| SPIDERLY019 | Error | [WithOne] declared on both sides of a one-to-one |
| SPIDERLY020 | Error | [WithOne] inverse navigation does not exist on the principal |
| SPIDERLY021 | Error | [Required] on the principal navigation of a one-to-one is unenforceable |
| SPIDERLY022 | Error | Self-referential one-to-one is not supported |
SPIDERLY001
Controller type is not a discovered entity or DTO.
A controller action takes or returns a class that isn't marked with [SpiderlyDTO] and doesn't inherit from BusinessObject<T> / ReadonlyObject<T>. The generated Angular client would reference an undefined TypeScript type.
Fix: add [SpiderlyDTO] to the class, or make it inherit from a Spiderly entity base class.
SPIDERLY002
Many-to-many entity requires exactly two [M2MWithMany] properties.
An entity declared as a many-to-many join has zero, one, or more than two [M2MWithMany] attributes. A join needs exactly one on each side.
Fix: annotate each of the two navigation properties on the join entity with [M2MWithMany] pointing at the opposite side. See Relationships for a worked example.
SPIDERLY003
[ForeignKey] references a property that does not exist.
[ForeignKey(nameof(X))] on a navigation or scalar points at a member name that isn't declared on the entity.
Fix: correct the nameof(...) argument, or add the missing scalar/navigation property.
SPIDERLY004
Foreign key type does not match target primary key.
The FK scalar's type (e.g. int) doesn't match the type of the target entity's Id (e.g. long).
Fix: change the FK scalar to the target entity's Id type. The primary key type comes from the entity's BusinessObject<T> / ReadonlyObject<T> base.
SPIDERLY005
Foreign key is ambiguous — multiple convention matches.
Multiple scalar properties match the {NavigationName}Id convention for the same navigation, so the generator cannot pick a unique FK.
Fix: use [ForeignKey(nameof(ExplicitFkScalar))] on the navigation to disambiguate.
SPIDERLY006
Foreign key nullability does not match navigation property.
Either a [Required] navigation is paired with a nullable FK scalar, or an optional navigation is paired with a non-nullable FK scalar.
Fix: align them. [Required] Category Category must pair with non-nullable long CategoryId; optional Category Category must pair with nullable long? CategoryId.
SPIDERLY007
[DisplayName] path references a property that does not exist.
A segment of [DisplayName("A.B.C")] names a property that isn't declared on the corresponding entity.
Fix: correct the path, or add the missing property.
SPIDERLY008
[DisplayName] path segment is not a many-to-one navigation.
An intermediate segment of a [DisplayName] path must be a many-to-one navigation so the generator can follow the chain.
Fix: use only M2O navigations for intermediate segments. Scalars and collection properties are only valid as the final segment.
SPIDERLY009
[DisplayName] navigation target entity not found.
The entity type referenced by a [DisplayName] navigation segment is not discovered in the current project or any referenced project with a .Entities namespace.
Fix: ensure the target entity lives in a .Entities namespace and its project is referenced.
SPIDERLY010
Entity missing required BusinessObject<T> / ReadonlyObject<T> base.
Every Spiderly entity must inherit — directly or transitively — from BusinessObject<T> or ReadonlyObject<T>. The generators cannot resolve the entity's Id type otherwise.
Fix: add the appropriate base class. See Add New Entity.
SPIDERLY011
Controller property type is not resolvable for client generation.
A generated controller method references a navigation property whose target entity cannot be discovered, so the Angular autocomplete / dropdown method cannot be generated.
Fix: ensure the target entity exists in a .Entities namespace and its project is referenced.
SPIDERLY012
One-to-many back-reference missing [M2MWithMany].
An entity declares a one-to-many collection into a complex many-to-many join, but the join entity has no property annotated with a matching [M2MWithMany(nameof(...))] pointing back.
Fix: add [M2MWithMany(nameof(ThisCollection))] to the corresponding navigation on the join entity.
SPIDERLY013
Backend folder not found under calling project.
The file-emitting generators (Angular entities/controllers/validators/details/enums) walk up from the calling project to find a Backend folder as an anchor for writing output. They emit this warning and skip file emission if the folder can't be found.
Fix: ensure your backend project lives under a folder named Backend (or pass a custom name if your generator consumer supports it).
SPIDERLY014
Blob property missing [AcceptedFileTypes] attribute.
Every blob property (any property decorated with a StorageAttribute subclass like [S3PublicStorage], [S3PrivateStorage], [DiskStorage], or a custom one) must declare [AcceptedFileTypes("mime/type", ...)] with at least one MIME-typed value. There is no implicit default — the old images-only fallback has been removed so every blob property's upload whitelist is an explicit, auditable decision at the source level.
An attribute that contains only extension values (e.g. [AcceptedFileTypes(".pdf")]) also trips this because the generator filters to MIME-typed entries (values containing /) when emitting the server-side validator.
Fix: add [AcceptedFileTypes(...)] with one or more MIME types. Examples:
[S3PublicStorage]
[AcceptedFileTypes("image/jpeg", "image/png", "image/webp", "image/avif")]
public string ImageUrl { get; set; }
[S3PublicStorage]
[AcceptedFileTypes("application/pdf", ".pdf")]
public string PdfFile { get; set; }SPIDERLY015
Many-to-one navigation missing [WithMany] attribute.
A virtual navigation property on a [SpiderlyEntity] class targets another entity (a many-to-one relationship), but is missing the required [WithMany(nameof(Target.Collection))] attribute. Without it, ApplicationDbContext cannot configure the HasOne(...).WithMany(...) relationship and the parent collection it points at.
Fix: add [WithMany(nameof(Target.Collection))] to the navigation, and declare public virtual List<Source> Collection { get; } = new(); on the target entity if it doesn't exist yet. If the relationship really should be unidirectional, drop the virtual navigation property entirely and configure the relationship manually via a partial OnModelCreating — see Relationships for the worked pattern.
SPIDERLY016
[WithMany] target collection does not exist on the related entity.
[WithMany("OrderItems")] (or [WithMany(nameof(Order.OrderItems))]) is declared on a many-to-one navigation, but the named collection property doesn't exist on the target entity. EF Core would die mid-OnModelCreating with a generic "no such navigation" error; this diagnostic surfaces it at build time with the offending file and line.
Fix: add the named collection (public virtual List<Source> OrderItems { get; } = new();) on the target entity, or correct the [WithMany] argument to match an existing collection name.
SPIDERLY017
[WithMany] target collection has the wrong element type.
[WithMany("OrderItems")] resolves to an existing property on the target entity, but the property's collection element type doesn't match the source entity. For example: [WithMany(nameof(User.OrderItems))] on OrderItem.User, but User.OrderItems is declared as List<Order> rather than List<OrderItem>. Typical cause: a copy-paste mistake or a rename that updated one side but not the other.
Fix: align the back-collection's element type with the source entity (List<OrderItem>), or change the [WithMany] target to a collection of the correct element type.
SPIDERLY018
Entity primary key type must be int, long, or byte.
An entity inherits BusinessObject<T> or ReadonlyObject<T> where T is something other than int, long, or byte (e.g. Guid, decimal, short, DateTime). The C# generic constraint on the base class is where T : struct, which technically admits any value type — but the rest of Spiderly (audit/versioning in ApplicationDbContext, FilterDTO.AdditionalFilterIdInt/Long/Byte, Angular type mapping) only handles the three documented types. Allowing anything else would silently produce broken generated code or, worse, dropped audit rows at runtime.
Fix: change T to int, long, or byte. If you need a public, non-enumerable identifier (UUID-style URLs for security/anti-enumeration), keep the numeric Id as the primary key and add a separate Guid PublicId property alongside it — Guid is a fully supported non-PK base data type.
// ✗ Rejected by SPIDERLY018
public class Order : BusinessObject<Guid> { }
// ✓ Recommended pattern for public-facing identifiers
public class Order : BusinessObject<long>
{
public Guid PublicId { get; set; }
}SPIDERLY019
[WithOne] declared on both sides of a one-to-one.
A one-to-one is declared by placing [WithOne] on the dependent (foreign-key-holding) side only — that single placement is what designates which side owns the FK. If both navigations carry [WithOne], the dependent side is ambiguous.
Fix: keep [WithOne] on the dependent and make the principal a plain single-valued navigation with no attribute.
SPIDERLY020
[WithOne] inverse navigation does not exist on the principal.
[WithOne(nameof(Principal.InverseNav))] names the back-navigation on the principal entity, but the named property doesn't exist (or isn't a single-valued navigation of the dependent's type).
Fix: add public virtual {Dependent} {InverseNav} { get; set; } on the principal, or use the parameterless [WithOne] for a unidirectional one-to-one.
SPIDERLY021
[Required] on the principal navigation of a one-to-one is unenforceable.
A unique foreign-key index guarantees at most one dependent per principal — never at least one. "Every principal has a dependent" cannot be enforced by the schema, so [Required] on the principal-side navigation is meaningless.
Fix: configure requiredness on the dependent ([WithOne]) side — a non-nullable FK means the dependent must have a principal. If you genuinely need "every principal has a dependent", create the dependent in the principal's OnAfter{Entity}Insert hook.
SPIDERLY022
Self-referential one-to-one is not supported.
A [WithOne] navigation whose target type is the declaring entity itself is rejected in this version.
Fix: model it differently (e.g. a self-referential many-to-one), or split the data into two entities.
Suppressing Diagnostics
To suppress a specific diagnostic project-wide, add it to <NoWarn> in the consuming project's .csproj:
<PropertyGroup>
<NoWarn>$(NoWarn);SPIDERLY013</NoWarn>
</PropertyGroup>Suppress at a single site with a #pragma:
#pragma warning disable SPIDERLY013
// ...code that triggers the diagnostic...
#pragma warning restore SPIDERLY013Use sparingly — each diagnostic points at a real contract violation that will produce broken generated code.