Skip to main content

Error Handling

This document describes the unified error handling approach in TUMApply, including how exceptions are structured, when to use which type, and what response clients can expect from the API.


πŸ“¦ API Error Response Format​

All handled exceptions in TUMApply return a structured JSON object like this:

{
"timestamp": "2025-05-13T14:12:45.123Z",
"status": 400,
"error": "Bad Request",
"message": "Field must not be null",
"path": "/api/users",
"errorCode": "VALIDATION_ERROR",
"fieldErrors": [
{
"objectName": "CreateUserRequest",
"field": "email",
"message": "must not be null"
}
]
}

🧱 Exception Classes Overview​

ClassHTTP StatusUse Case Description
AccessDeniedException403 FORBIDDENUser is authenticated but not authorized
EntityNotFoundException404 NOT_FOUNDEntity not found in the database
InternalServerException500 INTERNAL_ERRORUnhandled application error
InvalidParameterException400 BAD_REQUESTManually detected invalid input
MailingException500 INTERNAL_ERRORSending an email failed
OperationNotAllowedException400 BAD_REQUESTAction is not permitted in current context
ResourceAlreadyExistsException409 CONFLICTEntity with conflicting value already exists
UnauthorizedException401 UNAUTHORIZEDUser is not authenticated
UploadException400 BAD_REQUESTFile upload failed
Validation Error (Spring built-in)400 BAD_REQUESTAutomatically triggered by @Valid, shows fieldErrors

βœ… When to Use Which Exception​

SituationException
Entity with given ID not foundEntityNotFoundException.forId("User", userId)
Email already in useResourceAlreadyExistsException("Email already exists")
Missing or invalid manual inputInvalidParameterException("Invalid start date")
Business rule not metOperationNotAllowedException("Status change not allowed")
User is logged in but lacks permissionAccessDeniedException("Only admins can delete users")
User is not logged in or token expiredUnauthorizedException("Login required")
PDF file upload failedUploadException("Invalid file format")
Email cannot be sentMailingException("SMTP server not available")
Unexpected logic errorInternalServerException("Uncaught exception", cause)
Input DTO with @Valid failsAutomatically handled β†’ results in VALIDATION_ERROR

🎯 How to Use in Code​

Use with Optional values​

User user = getOrThrow(userRepository.findById(id), () -> EntityNotFoundException.forId("User", id));

Manual input validation​

if(!email.contains("@")){
throw new

InvalidParameterException("Invalid email format");
}

🧠 ErrorCode Enum​

Each handled exception maps to a unique ErrorCode enum value used in API responses:

public enum ErrorCode {
ACCESS_DENIED,
ENTITY_NOT_FOUND,
INTERNAL_ERROR,
INVALID_PARAMETER,
MAILING_ERROR,
OPERATION_NOT_ALLOWED,
RESOURCE_ALREADY_EXISTS,
UNAUTHORIZED,
UPLOAD_FAILED,
VALIDATION_ERROR,
}

These are included in the ApiError to help the client differentiate and react accordingly.


🧩 Field-Level Validation Errors​

If a request fails validation (e.g. @NotNull, @Size) the response includes fieldErrors, a list of:

{
"objectName": "CreateUserRequest",
"field": "email",
"message": "must not be null"
}

These are represented by the ValidationFieldError class.


πŸ§ͺ Testing Exceptions​

There are automated tests under GlobalExceptionHandlerTest.java that verify:

  • All exceptions are properly mapped to HTTP status codes
  • ApiError JSON structure is correct
  • errorCode and fieldErrors are returned correctly

πŸ“˜ Best Practices​

Best Practices
  • Use OptionalUtils.getOrThrow(...) to fetch entities cleanly.
  • Only throw exceptions that are semantically meaningful.
  • Prefer custom exceptions over generic ones.
  • Include ErrorCode consistently.
  • Add unit tests for expected exception behavior.