Validation and errors
The global pipe rejects unknown properties and transforms payloads into DTO instances before the controller. Failures are HTTP 400.
ResponseExceptionFilter catches everything else:
| Throw | Client sees |
|---|---|
HttpException | That status. Message translated by MessageService |
BadRequestException with an array | 400. The array is in the body only when app.debug is true |
| Any other error | 500 and a generic message. Stack only when debug is on |
Status 500 and above is logged and sent to Sentry.
Use i18n keys (the auth.error.staleSession and ai.error.workspaceDisabled style) rather than pasting English into an exception if the surrounding module already does that.
Forgot-password is an intentional non-oracle: success even when the email is unknown. Do not “fix” that into a 404.
For queue and outbox failures, the HTTP client is already gone. See Error handling.
Logging
Pino via nestjs-pino. APP_LOG_LEVEL controls it. Containers force JSON and default the level to info.