Skip to main content
Version: v0.11.0

Error Handling

Okapi provides a flexible error handling system with built-in support for standard JSON errors, custom error formats, and RFC 7807 Problem Details.

Note on error details in production

DefaultErrorHandler puts the underlying error's text into the details field of the response, and middleware such as the JWT authenticator passes its internal error through. Clients therefore learn exactly why a request failed — with a JWKS-backed setup that can include the JWKS URL and the network error behind it, which is useful reconnaissance against an internal endpoint.

If that matters for your deployment, install a custom ErrorHandler that logs the error server-side and omits details from the response. See Custom Error Handlers below.

Quick Start​

Use c.Abort* methods to immediately stop request processing and return an error response:

o := okapi.Default()

o.Post("/books", func(c okapi.C) error {
book := &Book{}
if err := c.Bind(book); err != nil {
return c.AbortBadRequest("Invalid request body", err)
}
// ... handle valid request
return c.Created(book)
})

Response:

{
"code": 400,
"message": "Invalid request body",
"details": "field Name is required",
"timestamp": "2026-02-09T21:34:17.290908+01:00"
}

Available Error Methods​

Okapi provides convenience methods for common HTTP error codes:

MethodStatus CodeUse Case
AbortBadRequest(msg, err)400Validation errors, malformed requests
AbortUnauthorized(msg, err)401Missing or invalid authentication
AbortForbidden(msg, err)403Insufficient permissions
AbortNotFound(msg, err)404Resource not found
AbortConflict(msg, err)409Resource conflicts
AbortInternalServerError(msg, err)500Unexpected server errors
And more

For other status codes, use the generic method:

return c.AbortWithError(http.StatusTeapot, err)

Custom Error Handlers​

Override the default error format by providing a custom error handler:

o := okapi.Default().With(
okapi.WithErrorHandler(func(c *okapi.Context, code int, message string, err error) error {
return c.JSON(code, map[string]any{
"success": false,
"error": map[string]any{
"code": code,
"message": message,
"details": err.Error(),
},
})
}),
)

Response:

{
"success": false,
"error": {
"code": 400,
"message": "Invalid request body"
}
}

RFC 7807 Problem Details​

For APIs requiring standards-compliant error responses, Okapi supports RFC 7807 Problem Details.

Basic Setup​

o := okapi.Default()
o.WithSimpleProblemDetailErrorHandler()

Response (Content-Type: application/problem+json):

{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "field Name is required",
"instance": "/books"
}

Advanced Configuration​

Customize the Problem Details output with additional fields and options:

o := okapi.Default()
o.WithProblemDetailErrorHandler(&okapi.ErrorHandlerConfig{
Format: okapi.ErrorFormatProblemJSON,
TypePrefix: "https://api.example.com/errors/",
IncludeInstance: true,
IncludeTimestamp: true,
CustomFields: map[string]any{
"api_version": "v1.0.0",
"support_url": "https://support.example.com",
},
})

Response:

{
"type": "https://api.example.com/errors/bad-request",
"title": "Bad Request",
"status": 400,
"detail": "field Name is required",
"instance": "/books",
"timestamp": "2026-02-09T21:42:49+01:00",
"api_version": "v1.0.0",
"support_url": "https://support.example.com"
}

Configuration Options​

OptionDescription
FormatResponse format (see below)
TypePrefixBase URL for error type URIs
IncludeInstanceInclude the request path in responses
IncludeTimestampAdd a timestamp to each error
CustomFieldsAdditional fields to include in all error responses

Supported Formats​

FormatContent-TypeDescription
ErrorFormatProblemJSONapplication/problem+jsonRFC 7807 JSON format (default)
ErrorFormatProblemXMLapplication/problem+xmlRFC 7807 XML format

Example with XML format:

o.WithProblemDetailErrorHandler(&okapi.ErrorHandlerConfig{
Format: okapi.ErrorFormatProblemXML,
})

Response (Content-Type: application/problem+xml):

<?xml version="1.0" encoding="UTF-8"?>
<problem xmlns="urn:ietf:rfc:7807">
<type>about:blank</type>
<title>Bad Request</title>
<status>400</status>
<detail>field Name is required</detail>
<instance>/books</instance>
</problem>