leeovery/agentic-skills/laravel-exceptions
Claude Code and agent skill by leeovery/agentic-skills
- community
Checking that the SKILL.md file is still in the repository.
Domain exceptions are one class per entity with named static factories; those that can reach a client carry their own status code and reporting rule. Use when throwing from an action guard, adding an exception factory, or deciding how an exception renders and reports.
Install
npx vskill@latest install leeovery/agentic-skills/laravel-exceptionsSKILL.md
Laravel Exceptions
A broken business rule is a domain exception, thrown by the action that owns the rule. Each entity has one exception class whose static factories name every way its operations can fail. An exception that can surface from a request implements Symfony's HttpExceptionInterface through the Httpable concern, so it carries its own status code and decides its own reporting, and the exception handler needs no wiring for it.
Rules
- One class per entity, a named factory per failure.
{Entity}Exceptioninapp/Exceptions/. Factories read as the failure:cannot{Verb}()for a business rule (cannotCancel()),failedTo{Verb}()for an operation that went wrong,{thing}NotFound()for something missing that isn't a model. Callers nevernewthe exception or pass it a message. - Guards throw the factory.
throw_unless($condition, OrderException::cannotCancel())in the action'sguard(). laravel-actions owns guards. - Exceptions that can surface from a request implement `HttpExceptionInterface` and use `Httpable`. Laravel then renders them with their own status, headers and message.
- Every HTTP factory passes its status code. Pick the code that fits the failure; there's no fixed table. The concern falls back to 500 for a missing code, which turns an expected failure into a server error.
- Write the message for the client. Laravel sends an
HttpExceptionInterfaceexception's message to the client even withAPP_DEBUGoff, 5xx included; every other exception becomesServer Error. Give a sentence the caller can act on, with public identifiers only: a fixed sentence, orsprintfwith an(ulid: %s)suffix. Never a path, a query, a provider's raw error or the cause's message. - Chain the cause. A factory that wraps a caught exception takes
Throwable $previousand passes it as the third constructor argument. The report keeps the original trace; the client sees only your message. - Client errors aren't reported during a request; server errors always are. A 4xx during a request is expected: the client has been told and nothing is broken. A 5xx means something is broken whoever called. In a job or scheduled task there's no client, so every failure needs attention.
Httpable::report()does this, and it must returntrueto skip reporting: Laravel treats any return value exceptfalseas "handled" and skips its own reporting, the error tracker's hook included. - Control-flow exceptions aren't HTTP. An exception thrown and caught inside a job to choose a path extends
Exceptionwith no interface and no concern. It never reaches a client. - Let the framework map its own failures. A missing model is
findOrFail()or route binding (404), a denied policy isAuthorizationException(403), invalid input is a Form Request (422). Don't write factories that duplicate them. - Nothing per exception in the `ExceptionBooter`. It holds the error tracker's hook; the interface and the concern do the mapping, so no
render()orreport()callbacks for domain exceptions. laravel-providers owns booters.
Example
The Httpable concern
One per project, in app/Exceptions/Concerns/. If the project already has one, use it, but check its report(): one that returns runningInConsole() is inverted, reporting every client error during requests and silencing every failure in queues.
The handler resolves report() through the container, so it can take the Application. Use ?: for the status fallback, not ??: Exception::$code is an int that defaults to 0, never null, and Symfony rejects status 0.
Reach for these when the situation calls for it
An action rejects a specific input field. When a rule only the action can check (stock, a combination of records) belongs to one field, subclass ValidationException with factories built on withMessages(). It renders as a 422 with field errors, exactly like a Form Request failure, and isn't reported.
Testing
Assert the class and the factory's message, so changing the text doesn't break the test:
In an HTTP test, assert the status and the same message: ->assertConflict()->assertJsonPath('message', OrderException::cannotCancel()->getMessage()). More in laravel-testing.
Anti-patterns
- ❌
return $this->code ?? 500;: the fallback never fires. - ❌
report()returning$app->runningInConsole(). - ❌
abort(409, '...')orabort_if()in an action. It ties the action to HTTP and loses the named failure. - ❌
throw new OrderException('...')at the call site, or a factory that takes the message as an argument. - ❌ A message carrying a file path, an internal id, SQL or
$previous->getMessage(). - ❌ An
OrderException::notFound()doing whatfindOrFail()already does. - ❌
$exceptions->render(fn (OrderException $e) => ...)in theExceptionBooter. - ❌
HttpExceptionInterfaceon an exception that never leaves a job.
Related
- laravel-actions: the guards that throw these exceptions
- laravel-state-machines: state preconditions behind
cannot{Verb}() - laravel-validation: input validation in Form Requests
- laravel-providers: the
ExceptionBooterand the error tracker's hook - laravel-testing: testing style
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckinglaravel/skills/laravel-exceptions/SKILL.md
Popularity
- Repository stars
- 14
- Repository forks
- 4
- 7-day trend score
- 0
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/leeovery/agentic-skills/laravel-exceptions)