Skip to content

Errors & exception handling

A global GenieExceptionHandler (an IExceptionHandler, registered by AddGenie and activated by app.UseGenieExceptionHandler() first in the pipeline) turns any unhandled exception into a consistent JSON envelope with a status code mapped from the exception type — instead of the framework’s default HTML error page or an empty 500.

{
"success": false,
"error": "The requested object was not found.",
"traceId": "00-4bf9…-01"
}
  • success is always false for an error response.
  • error is the message (see detail exposure for when 5xx messages are masked).
  • traceId is the current activity id (or the request trace identifier) — quote it in bug reports; the full failure is always logged server-side against the same id.

When IncludeStackTrace is on, the body also carries exceptionType and stackTrace for local debugging.

Throw the right exception type from your services and let the handler shape the response — don’t hand-roll BadRequest(new { message }) in new code.

Exception type Status
UnauthorizedException, UnauthorizedAccessException 401
NotFoundException, KeyNotFoundException 404
ArgumentException 400
ValidationException, RuntimeException, GenieException, InvalidOperationException 400
anything else 500

Expected (4xx) errors are deliberate, safe feedback, so they always carry their real message. Only unexpected (5xx) failures are subject to detail masking.

Whether 5xx bodies expose the real exception message is config-driven via the Genie:Errors section (GenieErrorOptions) — on in dev, off in prod:

"Genie": {
"Errors": {
"ExposeErrorDetails": true,
"IncludeStackTrace": false
}
}
Key Default Effect
ExposeErrorDetails true When true, a 5xx returns the real exception message. When false, it returns a generic “An unexpected error occurred…” message and logs the detail only. Turn off in production.
IncludeStackTrace false When true, the response also includes the exception type and full stack trace. Local debugging only — keep false outside Development.

Override in code through the builder:

builder.Services.AddGenie<YourContext>(genie => genie
.LoadFromConfiguration(builder.Configuration)
.ConfigureErrors(e =>
{
e.ExposeErrorDetails = false; // production
e.IncludeStackTrace = false;
}));