API errors
Document machine-readable API errors from Spatie Data classes, one class per failure mode. Point the generator at your abstract error base class:
'errors' => [ 'base_class' => \App\Http\Errors\ApiError::class,],The error class contract
Section titled “The error class contract”abstract class ApiError extends Data {}
class NotFoundError extends ApiError{ public const CODE = 'not_found'; public const MESSAGE = 'Resource not found'; public const STATUS = 404; // or an int-backed enum case}
class ProjectNotFoundError extends NotFoundError{ public const CODE = 'project_not_found'; public const MESSAGE = 'No project with that id'; // STATUS is inherited from NotFoundError}| Constant | Type | Rule |
|---|---|---|
CODE | string | The slug clients branch on. Every concrete error class declares its own. |
MESSAGE | string | The default human message, and the documentation text. May contain {marker} placeholders. |
STATUS | int, or an int-backed enum | The HTTP status. May be inherited. |
Generation fails before anything is written when:
- a concrete error class inherits
CODEorMESSAGEinstead of declaring it; - two error classes declare the same
CODE; - no
STATUScan be resolved, or it is not an HTTP status between 100 and 599; - an error class carries a class-level
#[Description](MESSAGEalready documents it); - an
#[EnvelopeField]property reuses an error-object field name; MESSAGEcontains a{marker}that names no public property of the class.
Public properties are the error’s typed details. #[EnvelopeField] lifts a property to the top level of the error object (e.g. a validation errors map). #[Throws] on a method or controller class declares which errors an action can return.
The error response
Section titled “The error response”{ "status": false, "data": [], "error": { "message": "Insufficient balance to complete this request", "code": "insufficient_balance", "template": "Insufficient balance to complete this request", "details": { "required": 500, "available": 120 } }}The data key suits apps whose success and error responses share one envelope. If yours doesn’t, set response_fields.data to null and error responses become { status, error }.
For each referenced error the generator emits:
InsufficientBalanceError— details schema (omitted when there are none)InsufficientBalanceErrorBody— the error objectInsufficientBalanceErrorResponse— the envelopecomponents.responses.InsufficientBalanceError— reusable response withx-http-statusErrorCode— string enum of the referenced codes
Only referenced errors are documented. An error no operation can return is an internal failure mode, not API surface.
Message templates
Section titled “Message templates”MESSAGE may contain {marker} placeholders so a client can translate it and then fill in the values. A marker must name a public property of the class. The error body then carries template (verbatim), params (one property per marker), and message (filled in).
Attaching errors to operations
Section titled “Attaching errors to operations”#[Throws] on the action is the explicit list. On a controller class it applies to every action:
use Langsys\OpenApiDocsGenerator\Generators\Attributes\Throws;
class ProjectController{ #[OA\Post(path: '/api/projects/{project}/purchase', responses: [...])] #[Throws(InsufficientBalanceError::class, ValidationError::class)] public function purchase(Project $project, PurchaseRequest $request) { … }}Errors can also be implied so they never have to be repeated. Configure implied_errors per documentation set:
'implied_errors' => [ 'middleware' => [ 'auth:sanctum' => [UnauthenticatedError::class], 'deduct.request' => [InsufficientBalanceError::class], ], 'validation' => ValidationFailedError::class, 'not_found' => NotFoundError::class, 'not_found_binding' => 'model', // or 'any'],When a convention lives somewhere the framework cannot prove, write a rule implementing Contracts\ImpliedErrorRule. Middleware is matched against the route’s fully-resolved middleware — the same ground truth filtered sets use.
An operation’s errors are the union of all sources, deduplicated by class, then grouped by HTTP status. One error for a status emits a $ref; several emit an inline oneOf with a code discriminator. A response the author wrote for that status always wins.