Skip to content

Implement global exception handling with custom error responses and e…#3

Merged
codebyNorthsteep merged 6 commits into
mainfrom
enhancement/global-exception-handler
May 6, 2026
Merged

Implement global exception handling with custom error responses and e…#3
codebyNorthsteep merged 6 commits into
mainfrom
enhancement/global-exception-handler

Conversation

@codebyNorthsteep

@codebyNorthsteep codebyNorthsteep commented May 5, 2026

Copy link
Copy Markdown
Owner

This pull request introduces a robust global exception handling mechanism for the backend, improves error reporting to the frontend, and enhances code clarity with comments and minor refactoring. The main focus is on better error management and user feedback for both developers and end-users.

Backend: Exception Handling and Validation

  • Added a global exception handler (GlobalExceptionHandler) to standardize error responses for different exception types, including custom LLM errors, timeouts, bad requests, and unexpected errors. This ensures clients receive clear, structured error messages.
  • Introduced a custom exception class LLMException to encapsulate errors from LLM providers, and refactored ChatService to throw this exception for LLM-related issues instead of generic runtime exceptions. [1] [2]
  • Added a standard error response record (ApiErrorResponse) to unify the error response format sent to clients.
  • Included the spring-boot-starter-validation dependency in pom.xml to support request validation.

Frontend: Improved Error Handling and Code Clarity

  • Enhanced error handling in app.js by parsing JSON error responses from the backend and displaying user-friendly error messages. Added explanatory comments and minor code clarifications for maintainability. [1] [2] [3] [4]…nhance error management in chat service

Summary by CodeRabbit

  • New Features

    • Centralized API error responses include timestamps and HTTP status codes; handlers cover external-service errors, timeouts, validation failures, and malformed requests.
  • Bug Fixes

    • Service surfaces HTTP error statuses and treats empty/invalid upstream responses as errors.
    • UI: session included in requests; loading and error states are reliably cleaned up and displayed.
  • Chores

    • Added Jakarta Bean Validation support.

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@codebyNorthsteep has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 28 minutes and 11 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 26db0bec-8e49-4d0b-89c3-f3107dfcbf5d

📥 Commits

Reviewing files that changed from the base of the PR and between 6e8f163 and 267f682.

📒 Files selected for processing (2)
  • src/main/java/org/example/projectbifrost/BifrostController.java
  • src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java
📝 Walkthrough

Walkthrough

Adds validation dependency; introduces ApiErrorResponse record and LLMException; updates ChatService to throw LLMException on error/invalid LLM responses; adds GlobalExceptionHandler mapping several exceptions to ApiErrorResponse responses; applies @Valid to controller and improves frontend timeout/error handling with sessionId wiring.

Changes

Structured error handling DAG

Layer / File(s) Summary
Dependencies / Data Shape
pom.xml, src/main/java/org/example/projectbifrost/exception/ApiErrorResponse.java
Adds spring-boot-starter-validation dependency and creates ApiErrorResponse(LocalDateTime timestamp, int status, String message) record.
Domain Exception Type
src/main/java/org/example/projectbifrost/exception/LLMException.java
Introduces LLMException extends RuntimeException with statusCode and model fields and constructor initializing them.
Service Error Handling
src/main/java/org/example/projectbifrost/service/ChatService.java
sendRequestToLLM attaches .onStatus(HttpStatusCode::isError, ...) to throw LLMException for error HTTP responses and throws LLMException for empty/invalid LLM responses.
Controller Validation
src/main/java/org/example/projectbifrost/BifrostController.java
Adds @Valid to sendChatRequest(@Valid @RequestBody ChatRequestDTO dto) and imports Jakarta validation.
Global Exception Mapping
src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java
New @ControllerAdvice with handlers: LLMException (derives/falls back status), ResourceAccessException → 504, MethodArgumentNotValidException → 400, HttpMessageNotReadableException → 400, and generic Exception → 500; responses use ApiErrorResponse.
Frontend wiring & UX
src/main/resources/static/app.js
sendMessage() adds sessionId to POST body, uses AbortController with 15s timeout (cleaned in finally), parses non-OK JSON error payloads (fallback to generic), and always resets loading state in finally.

Sequence Diagram

sequenceDiagram
    participant Client as Frontend
    participant Service as ChatService
    participant LLM as LLM Provider
    participant Handler as GlobalExceptionHandler

    Client->>Service: POST /chat (prompt, sessionId, model)
    Service->>LLM: RestClient POST (body)
    alt LLM returns error status
        LLM-->>Service: HTTP 4xx/5xx
        Service->>Service: onStatus(isError) -> throw LLMException
    else LLM returns empty/invalid body
        LLM-->>Service: 200 with empty/invalid body
        Service->>Service: throw LLMException
    else LLM returns success
        LLM-->>Service: 200 + content
        Service-->>Client: 200 + content
    end
    opt Exception path
        Service->>Handler: LLMException/other exception
        Handler->>Handler: map to HttpStatus, build ApiErrorResponse
        Handler-->>Client: ResponseEntity<ApiErrorResponse>
        Client->>Client: parse JSON error and display message
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

Poem

🐰 I munched a bug and found a plan,
Errors now named by rabbit-hand.
Timeouts hugged and schemas neat,
Responses tidy, logs concise and sweet.
Hoppity-hop — the app sleeps tight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: implementing global exception handling with custom error responses. It aligns with the primary purpose across all modified files (exception handlers, custom exceptions, and unified error responses).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch enhancement/global-exception-handler

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/main/java/org/example/projectbifrost/exception/ApiErrorResponse.java (1)

5-7: ⚡ Quick win

Prefer Instant over LocalDateTime for the timestamp field

LocalDateTime has no timezone information, so the value is ambiguous for any client not co-located with the server, and useless for cross-region log correlation.

♻️ Proposed change
-import java.time.LocalDateTime;
+import java.time.Instant;

 public record ApiErrorResponse(String message,
                                int status,
-                               LocalDateTime timestamp) {
+                               Instant timestamp) {
 }

All call sites in GlobalExceptionHandler then become Instant.now() instead of LocalDateTime.now().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/example/projectbifrost/exception/ApiErrorResponse.java`
around lines 5 - 7, ApiErrorResponse currently uses LocalDateTime for timestamp;
change the type to java.time.Instant (replace LocalDateTime with Instant in the
ApiErrorResponse record declaration) and update all call sites in
GlobalExceptionHandler to supply Instant.now() instead of LocalDateTime.now();
ensure imports are adjusted (import java.time.Instant) and any JSON
serialization/configuration still supports Instant (or add a serializer if
needed).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java`:
- Around line 45-53: The handler method handleBadRequestException in
GlobalExceptionHandler currently returns ex.getLocalizedMessage() for both
MethodArgumentNotValidException and HttpMessageNotReadableException which can
leak parser/internal class info; change it to differentiate the two cases —
either split into two `@ExceptionHandler` methods (one for
MethodArgumentNotValidException that includes the detailed localized message,
and one for HttpMessageNotReadableException that returns a generic parsing error
string) or keep the single method but instance-check ex (using instanceof
MethodArgumentNotValidException) and only use ex.getLocalizedMessage() for
validation errors while returning a safe generic message like "Malformed request
body" for HttpMessageNotReadableException before building ApiErrorResponse and
returning BAD_REQUEST.

In `@src/main/java/org/example/projectbifrost/service/ChatService.java`:
- Around line 58-60: The current onStatus handler in ChatService throws
LLMException with only a message and model, discarding the HTTP status and
breaking rate-limit handling; update LLMException to include an int/status Code
field (e.g., add a statusCode property, constructor, and getter), change
ChatService.where .onStatus(...) to throw the new LLMException carrying the
response.statusCode().value(), and update
GlobalExceptionHandler.handleLLMException to inspect
LLMException.getStatusCode() (use a switch or if-chain) to map 429 ->
TOO_MANY_REQUESTS, 401/403 -> UNAUTHORIZED/FORBIDDEN, 500 ->
SERVICE_UNAVAILABLE, etc., instead of relying on fragile string matching.

In `@src/main/resources/static/app.js`:
- Around line 67-75: The current try/catch around response.json() encloses the
throw new Error(errorData.message || 'The Gods are Silent') so that the inner
catch(jsonError) always intercepts it and replaces the backend message with
"Divine connection lost"; fix by separating JSON parsing from throwing: call
await response.json() inside a try, store errorData, and then outside that try
(or in a separate block) throw new Error(errorData.message || 'The Gods are
Silent'); keep the catch(jsonError) only for JSON parse failures and still throw
new Error("Divine connection lost", { cause: jsonError }) there so the backend
message isn't swallowed (identify the change around response.ok handling,
response.json(), jsonError, and the two throw sites).

---

Nitpick comments:
In `@src/main/java/org/example/projectbifrost/exception/ApiErrorResponse.java`:
- Around line 5-7: ApiErrorResponse currently uses LocalDateTime for timestamp;
change the type to java.time.Instant (replace LocalDateTime with Instant in the
ApiErrorResponse record declaration) and update all call sites in
GlobalExceptionHandler to supply Instant.now() instead of LocalDateTime.now();
ensure imports are adjusted (import java.time.Instant) and any JSON
serialization/configuration still supports Instant (or add a serializer if
needed).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7c8ffb96-dd75-4f72-a5be-8d824ebd69b4

📥 Commits

Reviewing files that changed from the base of the PR and between de876c3 and 173f0f7.

📒 Files selected for processing (6)
  • pom.xml
  • src/main/java/org/example/projectbifrost/exception/ApiErrorResponse.java
  • src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java
  • src/main/java/org/example/projectbifrost/exception/LLMException.java
  • src/main/java/org/example/projectbifrost/service/ChatService.java
  • src/main/resources/static/app.js

Comment thread src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java Outdated
Comment thread src/main/java/org/example/projectbifrost/service/ChatService.java
Comment thread src/main/resources/static/app.js
… improved logging; update ApiErrorResponse and LLMException for better error management

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java (1)

53-61: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a dedicated handler for HttpMessageNotReadableException — malformed JSON currently surfaces as 500.

With spring-boot-starter-validation now wired in, requests with malformed/unparseable JSON bodies (e.g., the frontend ever posts a syntactically invalid payload) throw HttpMessageNotReadableException, which is not a subtype of MethodArgumentNotValidException. As a result it falls through to handleGeneralException and the client receives 500 INTERNAL_SERVER_ERROR with the generic "An unexpected error occurred" — but this is a client-side input fault and should be 400 BAD_REQUEST.

🛠 Suggested handler (mirrors existing style; uses generic message to avoid leaking Jackson internals)
+    `@ExceptionHandler`(org.springframework.http.converter.HttpMessageNotReadableException.class)
+    public ResponseEntity<ApiErrorResponse> 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
+        );
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java`
around lines 53 - 61, Add a specific exception handler for
HttpMessageNotReadableException in the GlobalExceptionHandler class so
malformed/unparseable JSON returns 400 instead of falling through to
handleGeneralException; implement a method like public
ResponseEntity<ApiErrorResponse>
handleHttpMessageNotReadable(HttpMessageNotReadableException ex) that logs the
error (e.g., log.warn or log.error with context), and returns new
ResponseEntity<>(new ApiErrorResponse(LocalDateTime.now(), 400, "Malformed
request body"), HttpStatus.BAD_REQUEST) to mirror the existing ApiErrorResponse
usage and style while keeping the message generic.
🧹 Nitpick comments (3)
src/main/java/org/example/projectbifrost/exception/ApiErrorResponse.java (1)

5-10: 💤 Low value

LGTM — record shape matches all call sites in GlobalExceptionHandler.

The component order (timestamp, status, message) is consistent with every constructor invocation in GlobalExceptionHandler. Two very minor optional nits if you happen to revisit this file:

  • The blank line between the parameter list and the body block (line 8) is unusual; most formatters/IDEs will keep them adjacent.
  • Consider documenting the record at type level (Javadoc) rather than via a trailing inline comment on a single component, so all three fields are described uniformly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/example/projectbifrost/exception/ApiErrorResponse.java`
around lines 5 - 10, The ApiErrorResponse record currently has an unusual blank
line between the component list and its body and a trailing inline comment on
the message component; update the declaration of ApiErrorResponse to remove the
blank line so the parameter list and the body brace are adjacent, and add a
Javadoc comment on the ApiErrorResponse type describing the three components
(timestamp, status, message) instead of the inline comment; verify
GlobalExceptionHandler usages remain unchanged.
src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java (2)

38-58: ⚡ Quick win

Replace hardcoded status numbers with HttpStatus.*.value() for consistency.

Lines 38, 48 and 58 hardcode 504, 400 and 500 numerically inside ApiErrorResponse, while handleLLMException (line 29) uses status.value(). Using the enum throughout removes the risk of the body status drifting from the response status if anyone edits one but not the other.

♻️ Proposed cleanup
-                new ApiErrorResponse(LocalDateTime.now(), 504, "Connection timeout. The Gods are taking too long to respond."),
+                new ApiErrorResponse(LocalDateTime.now(), HttpStatus.GATEWAY_TIMEOUT.value(), "Connection timeout. The Gods are taking too long to respond."),
                 HttpStatus.GATEWAY_TIMEOUT
@@
-                new ApiErrorResponse(LocalDateTime.now(), 400, "Invalid request - check your input"),
+                new ApiErrorResponse(LocalDateTime.now(), HttpStatus.BAD_REQUEST.value(), "Invalid request - check your input"),
                 HttpStatus.BAD_REQUEST
@@
-                new ApiErrorResponse(LocalDateTime.now(), 500, "An unexpected error occurred"),
+                new ApiErrorResponse(LocalDateTime.now(), HttpStatus.INTERNAL_SERVER_ERROR.value(), "An unexpected error occurred"),
                 HttpStatus.INTERNAL_SERVER_ERROR
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java`
around lines 38 - 58, Replace the hardcoded numeric status codes in the
ApiErrorResponse instances with the corresponding HttpStatus enum values to keep
body and response consistent: in handleValidationException use
HttpStatus.BAD_REQUEST.value(), in handleGeneralException use
HttpStatus.INTERNAL_SERVER_ERROR.value(), and where the gateway timeout is
constructed use HttpStatus.GATEWAY_TIMEOUT.value(); update the ApiErrorResponse
constructor invocations in these methods (refer to handleValidationException,
handleGeneralException and the gateway timeout handling) so the numeric status
in the response body is derived from the HttpStatus enum instead of literal
integers.

17-32: 💤 Low value

LGTM — dynamic status mapping replaces fragile string inspection.

Mapping ex.getStatusCode() through HttpStatus.valueOf(...) with an IllegalArgumentException fallback to 500 is a clean replacement for the previous message.contains("429") approach. The log.warn line gives operators the upstream status for triage without leaking it through the response.

One minor defensive consideration: HttpStatus.valueOf(int) will happily accept any registered status code, including 1xx/2xx/3xx. If LLMException is ever constructed with a non-error status (or an upstream returns an unusual code like 200 alongside an "isError" classifier in a future change), the handler would echo that status with an error-shaped body. Optionally, you could clamp to 4xx/5xx:

🛡️ Optional defensive clamp
         try {
             status = HttpStatus.valueOf(ex.getStatusCode()); //Get the error from OpenRouter
+            if (!status.isError()) {
+                status = HttpStatus.INTERNAL_SERVER_ERROR;
+            }
         } catch (IllegalArgumentException e) {
             status = HttpStatus.INTERNAL_SERVER_ERROR;
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java`
around lines 17 - 32, The handler handleLLMException in GlobalExceptionHandler
currently uses HttpStatus.valueOf(ex.getStatusCode()) which may return non-error
codes; after resolving the status, enforce a defensive clamp so only 4xx/5xx are
returned to clients: call HttpStatus.valueOf(...), then
if(!status.is4xxClientError() && !status.is5xxServerError()) set status =
HttpStatus.INTERNAL_SERVER_ERROR (or another chosen 4xx/5xx) before logging and
building the ApiErrorResponse; keep log.warn and ApiErrorResponse usage but
ensure the ResponseEntity always uses the clamped status.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/resources/static/app.js`:
- Around line 67-85: The current catch in the async fetch flow (where you call
appendMessage('assistant', 'System', error.message)) displays raw
Abort/DOMException text; update the catch to detect an abort by checking
error.name === 'AbortError' (or error instanceof DOMException) and replace
error.message with a friendlier timeout message like "Request timed out — the
Gods will return shortly" before calling appendMessage('assistant', 'System',
...); ensure this logic sits in the same function handling the
fetch/AbortController (the block that reads response.json(), response.text(),
and calls setLoading(false)) so normal errors still render their messages and
aborts render the themed timeout text.

---

Duplicate comments:
In
`@src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java`:
- Around line 53-61: Add a specific exception handler for
HttpMessageNotReadableException in the GlobalExceptionHandler class so
malformed/unparseable JSON returns 400 instead of falling through to
handleGeneralException; implement a method like public
ResponseEntity<ApiErrorResponse>
handleHttpMessageNotReadable(HttpMessageNotReadableException ex) that logs the
error (e.g., log.warn or log.error with context), and returns new
ResponseEntity<>(new ApiErrorResponse(LocalDateTime.now(), 400, "Malformed
request body"), HttpStatus.BAD_REQUEST) to mirror the existing ApiErrorResponse
usage and style while keeping the message generic.

---

Nitpick comments:
In `@src/main/java/org/example/projectbifrost/exception/ApiErrorResponse.java`:
- Around line 5-10: The ApiErrorResponse record currently has an unusual blank
line between the component list and its body and a trailing inline comment on
the message component; update the declaration of ApiErrorResponse to remove the
blank line so the parameter list and the body brace are adjacent, and add a
Javadoc comment on the ApiErrorResponse type describing the three components
(timestamp, status, message) instead of the inline comment; verify
GlobalExceptionHandler usages remain unchanged.

In
`@src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java`:
- Around line 38-58: Replace the hardcoded numeric status codes in the
ApiErrorResponse instances with the corresponding HttpStatus enum values to keep
body and response consistent: in handleValidationException use
HttpStatus.BAD_REQUEST.value(), in handleGeneralException use
HttpStatus.INTERNAL_SERVER_ERROR.value(), and where the gateway timeout is
constructed use HttpStatus.GATEWAY_TIMEOUT.value(); update the ApiErrorResponse
constructor invocations in these methods (refer to handleValidationException,
handleGeneralException and the gateway timeout handling) so the numeric status
in the response body is derived from the HttpStatus enum instead of literal
integers.
- Around line 17-32: The handler handleLLMException in GlobalExceptionHandler
currently uses HttpStatus.valueOf(ex.getStatusCode()) which may return non-error
codes; after resolving the status, enforce a defensive clamp so only 4xx/5xx are
returned to clients: call HttpStatus.valueOf(...), then
if(!status.is4xxClientError() && !status.is5xxServerError()) set status =
HttpStatus.INTERNAL_SERVER_ERROR (or another chosen 4xx/5xx) before logging and
building the ApiErrorResponse; keep log.warn and ApiErrorResponse usage but
ensure the ResponseEntity always uses the clamped status.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0c884e1b-c736-4347-9cc5-bfccefbf8d1c

📥 Commits

Reviewing files that changed from the base of the PR and between 173f0f7 and 56862b8.

📒 Files selected for processing (5)
  • src/main/java/org/example/projectbifrost/exception/ApiErrorResponse.java
  • src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java
  • src/main/java/org/example/projectbifrost/exception/LLMException.java
  • src/main/java/org/example/projectbifrost/service/ChatService.java
  • src/main/resources/static/app.js

Comment thread src/main/resources/static/app.js
…outs and add handling for malformed request bodies
@codebyNorthsteep

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java (1)

34-51: 💤 Low value

Optional: use HttpStatus.*.value() consistently instead of magic ints.

handleLLMException uses status.value() (line 29) but the timeout/validation/general handlers hardcode 504, 400, 500. Aligning on the enum makes the code self-documenting and future-proof if status codes are ever bumped.

♻️ Proposed tweak
-                new ApiErrorResponse(LocalDateTime.now(), 504, "Connection timeout. The Gods are taking too long to respond."),
+                new ApiErrorResponse(LocalDateTime.now(), HttpStatus.GATEWAY_TIMEOUT.value(), "Connection timeout. The Gods are taking too long to respond."),
                 HttpStatus.GATEWAY_TIMEOUT
...
-                new ApiErrorResponse(LocalDateTime.now(), 400, "Invalid request - check your input"),
+                new ApiErrorResponse(LocalDateTime.now(), HttpStatus.BAD_REQUEST.value(), "Invalid request - check your input"),
                 HttpStatus.BAD_REQUEST
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java`
around lines 34 - 51, In GlobalExceptionHandler, several handlers
(handleTimeoutException, handleValidationException and any other handlers that
construct ApiErrorResponse with hardcoded integers) use magic ints like
504/400/500; change those to use the corresponding HttpStatus enum values (e.g.,
HttpStatus.GATEWAY_TIMEOUT.value(), HttpStatus.BAD_REQUEST.value(),
HttpStatus.INTERNAL_SERVER_ERROR.value()) so that the ApiErrorResponse is
created with HttpStatus.*.value() consistently (match the existing
handleLLMException pattern that uses status.value()) and keep the ResponseEntity
status using the HttpStatus enum as already done.
src/main/resources/static/app.js (1)

132-137: 💤 Low value

Optional: simplify the keydown handler.

sendMessage already swallows errors in its own try/catch/finally, so the outer .then(r => {}).catch(err => console.error(err)) will never fire on a normal flow and the empty .then is just noise. A bare call (or void sendMessage()) is equivalent and clearer.

♻️ Proposed tweak
-        sendMessage().then(r => {
-        }).catch(err => console.error(err));
+        void sendMessage();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/static/app.js` around lines 132 - 137, The keydown handler
calls sendMessage().then(...).catch(...) which is redundant because sendMessage
handles its own errors and the empty .then is noise; replace the chained promise
usage with a simple call (e.g., void sendMessage() or just sendMessage()) inside
the if block to simplify the handler while preserving e.preventDefault() and the
reference to dom.input event handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java`:
- Around line 63-70: The catch‑all handler in
GlobalExceptionHandler.handleGeneralException currently converts every Exception
into a 500 response and masks Spring’s framework status codes; change
GlobalExceptionHandler to extend ResponseEntityExceptionHandler so Spring’s
resolvers run first, and update handleGeneralException to respect
framework-provided statuses by checking for ResponseStatusException (or
inspecting ErrorResponse/HttpStatus from exceptions) and using that status when
present before defaulting to 500; keep logging via log.error("Unexpected error:
", ex) and construct ApiErrorResponse with the resolved status code and message.

---

Nitpick comments:
In
`@src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java`:
- Around line 34-51: In GlobalExceptionHandler, several handlers
(handleTimeoutException, handleValidationException and any other handlers that
construct ApiErrorResponse with hardcoded integers) use magic ints like
504/400/500; change those to use the corresponding HttpStatus enum values (e.g.,
HttpStatus.GATEWAY_TIMEOUT.value(), HttpStatus.BAD_REQUEST.value(),
HttpStatus.INTERNAL_SERVER_ERROR.value()) so that the ApiErrorResponse is
created with HttpStatus.*.value() consistently (match the existing
handleLLMException pattern that uses status.value()) and keep the ResponseEntity
status using the HttpStatus enum as already done.

In `@src/main/resources/static/app.js`:
- Around line 132-137: The keydown handler calls
sendMessage().then(...).catch(...) which is redundant because sendMessage
handles its own errors and the empty .then is noise; replace the chained promise
usage with a simple call (e.g., void sendMessage() or just sendMessage()) inside
the if block to simplify the handler while preserving e.preventDefault() and the
reference to dom.input event handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 21a474ea-ffb4-498b-96f4-40956d66d61c

📥 Commits

Reviewing files that changed from the base of the PR and between 56862b8 and 05412f6.

📒 Files selected for processing (2)
  • src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java
  • src/main/resources/static/app.js

…outs and add handling for malformed request bodies
…Handler inheritance and adding validation to chat request DTO

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/org/example/projectbifrost/BifrostController.java`:
- Around line 28-29: The handler in BifrostController is logging raw user
messages and session identifiers (logger.info in the request path) which is a
privacy/compliance risk; change the log to avoid dto.message() and to redact or
hash dto.sessionId() (e.g., implement a helper like maskSessionId or compute a
stable hash of sessionId) and log only non-sensitive metadata such as
personality and the redacted/hashed session id. Locate the logger.info call in
BifrostController and replace the arguments to exclude dto.message(), call your
maskSessionId(String) or hashSessionId(String) helper to produce the session
token shown in logs, and ensure the helper handles null/short values safely.

In
`@src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java`:
- Around line 68-73: The handler in GlobalExceptionHandler converts
ErrorResponse er.getStatusCode() to HttpStatus which throws
IllegalArgumentException for non-standard codes; instead read the existing
HttpStatusCode from ErrorResponse (use er.getStatusCode()), extract its int
value for ApiErrorResponse, and build the ResponseEntity using the
HttpStatusCode (or ResponseEntity.status(code).body(...)) so non-standard status
codes (e.g., 499) are handled without throwing; update the branch handling
ErrorResponse and use HttpStatusCode, ApiErrorResponse, and ResponseEntity
constructs accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0e807854-854a-404a-9183-3ebc40b793cf

📥 Commits

Reviewing files that changed from the base of the PR and between 05412f6 and 6e8f163.

📒 Files selected for processing (2)
  • src/main/java/org/example/projectbifrost/BifrostController.java
  • src/main/java/org/example/projectbifrost/exception/GlobalExceptionHandler.java

Comment thread src/main/java/org/example/projectbifrost/BifrostController.java Outdated
… and adding session ID masking in chat request logging
@codebyNorthsteep
codebyNorthsteep merged commit 90e79a5 into main May 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant