agentleFS
LeanMeanCleanAppsLeanMeanCleanApps/CleanModelContextProtocol/.agents/skills/errors-and-exceptions/SKILL.md

errors-and-exceptions

Skill1 starsChanged 6 days agoMITView on GitHub

How Application reports not found and invalid input with exceptions, and how the presentation layer turns them into responses. Use when a use case can fail, when adding a new error case, or when changing how errors reach the caller.

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 2 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Test the not-found path in the handler tests with Should.ThrowAsync<NotFoundException>.

    line 31
  2. Very specific:

    If you add a new exception type, map it in the presentation layer's error handler in the same change, and test the mapping.

    line 32
  3. Very specific:

    Test that the handler logs an unexpected exception once, as Error, and logs nothing for the ones it maps (unit-tests).

    line 33
  4. Distinctive:

    Check that a row exists and then update or delete it.

    line 61
  5. Distinctive:

    Put a stack trace, SQL or exception type name in a message.

    line 62

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Errors and exceptions

The rule

Application signals failure by throwing; it never returns error codes or result objects.

SituationThrown byException
Input breaks a ruleValidationBehaviour, from the validatorFluentValidation.ValidationException
Something referenced doesn't existThe handlerNotFoundException (src/Application/Common/Exceptions/NotFoundException.cs)
Anything elseWhereverLeft alone; the presentation layer reports it as an unexpected error

The presentation layer maps each exception to its own shape in one place (in a web API, an IExceptionHandler writing RFC 9457 problem details). Messages are written for the caller and never include stack traces or internal details.

LoggingBehaviour logs validation failures and not found as Information, because the caller got an answer. The presentation layer's handler logs anything unexpected as Error, with the exception, in the same place it reports it (serilog).

Steps

  1. For a missing entity, use the helpers rather than throw new:
    • NotFoundException.ThrowIfNull(result, EntityType.Movie); after a fetch, or after an update that returns the changed entity, or null when no row matched.
    • NotFoundException.Throw(EntityType.Review); when a delete changed no rows.
    • NotFoundException.Throw(EntityType.Author); after an existence check. Only check first when the write itself can't tell you which thing is missing, such as a new review's author and movie.
  2. A new kind of entity needs a value in EntityType (src/Application/Common/Enums/EntityType.cs).
  3. Test the not-found path in the handler tests with Should.ThrowAsync<NotFoundException>.
  4. If you add a new exception type, map it in the presentation layer's error handler in the same change, and test the mapping. If the caller should see it as an answer rather than a fault, log it in LoggingBehaviour too.
  5. Test that the handler logs an unexpected exception once, as Error, and logs nothing for the ones it maps (unit-tests).

Example

// src/Application/Reviews/Commands/UpdateReview/UpdateReviewHandler.cs
var review = await reviewsRepository.UpdateReview(request.Id, request.Stars, request.Text, cancellationToken);

NotFoundException.ThrowIfNull(review, EntityType.Review);

return review;
// src/Application/Reviews/Commands/CreateReview/CreateReviewHandler.cs
if (!await authorsRepository.AuthorExists(request.AuthorId, cancellationToken))
{
    NotFoundException.Throw(EntityType.Author);
}

Throw is marked [DoesNotReturn] and ThrowIfNull is marked [NotNull], so the compiler knows the value isn't null afterwards and nullable warnings stay quiet.

Don't

  • Return null, false or an empty result to mean "not found" from a handler.
  • Catch exceptions in a handler to turn them into something else.
  • Introduce a Result<T> type. We use exceptions on purpose.
  • Check that a row exists and then update or delete it. That's two trips and a race; let the write tell you.
  • Put a stack trace, SQL or exception type name in a message.

Check

dotnet test --filter "FullyQualifiedName~NotFound|FullyQualifiedName~ExceptionHandler"

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.