Exceptions

Learn about Spiderly's exception types and how the global exception handler maps them to HTTP responses.

Spiderly's global exception handler catches all exceptions and maps them to appropriate HTTP responses. Responses share a common ApiErrorDTO shape:

{
  statusCode: number;
  message: string;
  errorCode?: string;          // machine-readable discriminator (e.g. "invalid_token")
  fieldErrors?: { [field: string]: string[] };  // populated for 422 responses
  exception?: string;          // full stack trace, development only
}
ExceptionStatusLog LevelMessage to ClientNotification
BusinessException400InformationException messageNo
ExpiredVerificationException400InformationException messageNo
SpiderlyValidationException422InformationPer-field errorsNo
FluentValidation.ValidationException422InformationPer-field errorsNo
UnauthorizedException401WarningException messageNo
SecurityTokenException401 (RFC 6750)InformationException messageNo
SecurityViolationException403ErrorGeneric errorYes
DbUpdateConcurrencyException409WarningLocalized ConcurrencyExceptionNo
DbUpdateException (unique/FK violation)409WarningLocalized messageNo
Unhandled500ErrorGeneric errorYes

BusinessException

The primary way to reject invalid input with a user-facing message. Throw it from lifecycle hooks (e.g., OnBeforeProductInsert) to return a meaningful error to the client:

throw new BusinessException("Product name must be unique.");

Always returns 400 Bad Request. Use a different exception type if you need a different status — that's the whole point of semantic exceptions.

ExpiredVerificationException

Returns 400 Bad Request with the exception message, logged at Information level. Used internally by the security module when a verification code has expired.

SpiderlyValidationException

Throw when domain rules produce per-field errors that can't be expressed via FluentValidation attributes. Returns 422 Unprocessable Entity with a fieldErrors dictionary the client can render inline next to the offending inputs:

throw new SpiderlyValidationException(new Dictionary<string, string[]>
{
    ["Sku"] = new[] { "SKU already exists." }
});

Standard FluentValidation failures (ValidateAndThrow() from generated validators) are converted to the same response shape automatically — you don't need to catch and rethrow.

UnauthorizedException

For custom authorization checks — returns 401 Unauthorized with your message. See the Authorization Service section for a usage example.

SecurityTokenException

Thrown from refresh-token flows when the presented token is missing, expired, or tampered with. Returns 401 Unauthorized with:

  • WWW-Authenticate: Bearer error="invalid_token", error_description="..." (per RFC 6750)
  • errorCode: "invalid_token" in the body

The handler also clears the access, refresh, and auth-result cookies. Clients should check either the header or the errorCode and treat the response as a forced logout.

SecurityViolationException

For requests that indicate malicious intent (tampered IDs, forged hidden fields, file-size bypass attempts). Returns a generic error message (never revealing what went wrong) and triggers email/Telegram notifications so you can investigate. Used internally by Spiderly's generated services for integrity checks.

DbUpdateException

The handler recognises common Postgres and SQL Server constraint violations and maps them to 409 Conflict with localized messages:

DialectCodeMeaningLocalization key
Postgres23505Unique violationUniqueConstraintException
Postgres23503Foreign-key violationForeignKeyConstraintException
SQL Server2627, 2601Unique violationUniqueConstraintException
SQL Server547Foreign-key violationForeignKeyConstraintException

Other DbUpdateException instances fall through to the generic 500 handler. DbUpdateConcurrencyException is handled separately with the ConcurrencyException message.

Unhandled Exceptions

Any exception that doesn't match the types above returns a generic error message to the client (never exposing internal details) and triggers email/Telegram notifications in production via INotificationDispatcher.