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

IDSeverityTitle
SPIDERLY001ErrorController type is not a discovered entity or DTO
SPIDERLY002ErrorMany-to-many entity requires exactly two [M2MWithMany] properties
SPIDERLY003Error[ForeignKey] references a property that does not exist
SPIDERLY004ErrorForeign key type does not match target primary key
SPIDERLY005ErrorForeign key is ambiguous — multiple convention matches
SPIDERLY006ErrorForeign key nullability does not match navigation property
SPIDERLY007Error[DisplayName] path references a property that does not exist
SPIDERLY008Error[DisplayName] path segment is not a many-to-one navigation
SPIDERLY009Error[DisplayName] navigation target entity not found
SPIDERLY010ErrorEntity missing required BusinessObject<T> / ReadonlyObject<T> base
SPIDERLY011ErrorController property type is not resolvable for client generation
SPIDERLY012ErrorOne-to-many back-reference missing [M2MWithMany]
SPIDERLY013WarningBackend folder not found under calling project
SPIDERLY014ErrorBlob property missing [AcceptedFileTypes] attribute
SPIDERLY015ErrorMany-to-one navigation missing [WithMany] attribute
SPIDERLY016Error[WithMany] target collection does not exist on the related entity
SPIDERLY017Error[WithMany] target collection has the wrong element type
SPIDERLY018ErrorEntity primary key type must be int, long, or byte
SPIDERLY019Error[WithOne] declared on both sides of a one-to-one
SPIDERLY020Error[WithOne] inverse navigation does not exist on the principal
SPIDERLY021Error[Required] on the principal navigation of a one-to-one is unenforceable
SPIDERLY022ErrorSelf-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 SPIDERLY013

Use sparingly — each diagnostic points at a real contract violation that will produce broken generated code.