Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,86 @@

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;
import org.springframework.web.ErrorResponse;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.time.LocalDateTime;
import java.time.Instant;

@ControllerAdvice
@Slf4j //Structured logging
public class GlobalExceptionHandler {
private static final String TIMESTAMP_PROPERTY = "timestamp";

@ExceptionHandler(LLMException.class)
public ResponseEntity<ApiErrorResponse> handleLLMException(LLMException ex) {
public ResponseEntity<ProblemDetail> handleLLMException(LLMException ex) {
HttpStatus status;
try {
status = HttpStatus.valueOf(ex.getStatusCode()); //Get the error from OpenRouter
} catch (IllegalArgumentException e) {
} catch (IllegalArgumentException _) {
status = HttpStatus.INTERNAL_SERVER_ERROR;
}

log.warn("LLM Error [{}]: {}", status.value(), ex.getMessage());

return new ResponseEntity<>(
new ApiErrorResponse(LocalDateTime.now(), status.value(), ex.getMessage()),
status
);
ProblemDetail problem = ProblemDetail.forStatusAndDetail(status, ex.getMessage());
problem.setProperty(TIMESTAMP_PROPERTY, Instant.now().toString());
problem.setTitle("LLM Error");
return ResponseEntity.status(status).body(problem);
}

@ExceptionHandler(ResourceAccessException.class)
public ResponseEntity<ApiErrorResponse> handleTimeoutException(ResourceAccessException ex) {
public ResponseEntity<ProblemDetail> handleTimeoutException(ResourceAccessException ex) {
log.warn("Timeout connecting to LLM: {}", ex.getMessage());
return new ResponseEntity<>(
new ApiErrorResponse(LocalDateTime.now(), 504, "Connection timeout. The Gods are taking too long to respond."),
HttpStatus.GATEWAY_TIMEOUT
);

ProblemDetail problem = ProblemDetail.forStatusAndDetail(HttpStatus.GATEWAY_TIMEOUT, "Connection timeout. The Gods are taking too long to respond.");
problem.setProperty(TIMESTAMP_PROPERTY, Instant.now().toString());
problem.setTitle("Connection Timeout");
return ResponseEntity.status(HttpStatus.GATEWAY_TIMEOUT).body(problem);
}


@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {
public ResponseEntity<ProblemDetail> handleValidationException(MethodArgumentNotValidException ex) {
log.warn("Validation error - {} field(s) invalid", ex.getBindingResult().getErrorCount());
return new ResponseEntity<>(
new ApiErrorResponse(LocalDateTime.now(), 400, "Invalid request - check your input"),
HttpStatus.BAD_REQUEST
);

ProblemDetail problem = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "Invalid request - check your input");
problem.setProperty(TIMESTAMP_PROPERTY, Instant.now().toString());
problem.setTitle("Validation Error");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(problem);
}

@ExceptionHandler(org.springframework.http.converter.HttpMessageNotReadableException.class)
public ResponseEntity<ApiErrorResponse> handleUnreadableMessage(org.springframework.http.converter.HttpMessageNotReadableException ex) {
public ResponseEntity<ProblemDetail> handleUnreadableMessage(org.springframework.http.converter.HttpMessageNotReadableException ex) {
log.warn("Malformed request body: {}", ex.getMostSpecificCause().getMessage());
return new ResponseEntity<>(
new ApiErrorResponse(LocalDateTime.now(), HttpStatus.BAD_REQUEST.value(), "Malformed request body"),
HttpStatus.BAD_REQUEST
);

ProblemDetail problem = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "Malformed request body");
problem.setProperty(TIMESTAMP_PROPERTY, Instant.now().toString());
problem.setTitle("Malformed Request");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(problem);
}


@ExceptionHandler(Exception.class)
public ResponseEntity<ApiErrorResponse> handleGeneralException(Exception ex) {
public ResponseEntity<ProblemDetail> handleGeneralException(Exception ex) {
//If Spring has something to say about an error(e.g 404), use it!
if (ex instanceof ErrorResponse er) {
var status = er.getStatusCode();
String message = ex.getMessage() != null ? ex.getMessage() : "Request failed";
return ResponseEntity.status(status).body(
new ApiErrorResponse(LocalDateTime.now(), status.value(), message)
);
ProblemDetail problem = ProblemDetail.forStatusAndDetail(status, message);
problem.setProperty(TIMESTAMP_PROPERTY, Instant.now().toString());
problem.setTitle("Request Error");
return ResponseEntity.status(status).body(problem);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
log.error("Unexpected error: ", ex);
return new ResponseEntity<>(
new ApiErrorResponse(LocalDateTime.now(), 500, "An unexpected error occurred"),
HttpStatus.INTERNAL_SERVER_ERROR
);

ProblemDetail problem = ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, "An unexpected error occurred");
problem.setProperty(TIMESTAMP_PROPERTY, Instant.now().toString());
problem.setTitle("Internal Server Error");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(problem);
}
}
1 change: 1 addition & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
spring.application.name=ProjectBifrost
spring.mvc.problemdetails.enabled=true
openrouter.api.key=${OPENROUTER_API_KEY}
openrouter.model=poolside/laguna-xs.2:free