Skip to content

Bug: /api/cards/compare returns 400 due to routing conflict with /{id} path variable #84

Description

@yortch

Problem

Requests to GET /api/cards/compare are incorrectly matched by the @GetMapping("/{id}") endpoint in CreditCardController, causing a MethodArgumentTypeMismatchException when Spring attempts to convert the string "compare" to java.lang.Long. This results in HTTP 400 Bad Request errors and breaks the card comparison feature.

Evidence

Production logs (2026-04-25 ~00:01 UTC) — 3 occurrences within 10 seconds:

WARN DefaultHandlerExceptionResolver: Resolved [MethodArgumentTypeMismatchException:
Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long';
For input string: "compare"]

Detected by automated health check on ca-banking-demo-backend (revision ca-banking-demo-backend--azd-1774558182).

Root Cause

CreditCardController maps @GetMapping("/{id}") which acts as a catch-all for any path under /api/cards/. When /api/cards/compare is requested, Spring matches it to /{id} and fails the type conversion from String to Long.

Affected file: backend/src/main/java/com/threeriversbank/controller/CreditCardController.java (line 42)

Fix (Ready on branch fix/cards-compare-routing-bug)

1. New /compare endpoint (CreditCardController.java)

Add @GetMapping("/compare") mapped before /{id} to prevent route ambiguity. Accepts optional ids query parameter for targeted comparison:

@GetMapping("/compare")
@Operation(summary = "Compare credit cards")
public ResponseEntity<List<CreditCardDto>> compareCards(
        @RequestParam(required = false) List<Long> ids) {
    List<CreditCardDto> cards;
    if (ids != null && !ids.isEmpty()) {
        cards = creditCardService.getCardsByIds(ids);
    } else {
        cards = creditCardService.getAllCreditCards();
    }
    return ResponseEntity.ok(cards);
}

2. New service method (CreditCardService.java)

@Transactional(readOnly = true)
public List<CreditCardDto> getCardsByIds(List<Long> ids) {
    return creditCardRepository.findAllById(ids).stream()
            .map(this::convertToDtoWithDetails)
            .collect(Collectors.toList());
}

3. Global Exception Handler (GlobalExceptionHandler.java)

New @RestControllerAdvice class that handles MethodArgumentTypeMismatchException and RuntimeException with structured JSON error responses.

4. Unit tests (CreditCardControllerTest.java)

  • compareCards_WithIds_ShouldReturnSelectedCards
  • compareCards_WithoutIds_ShouldReturnAllCards

Impact

  • Severity: Medium — card comparison feature is broken for users navigating to /api/cards/compare
  • Scope: Backend only; frontend comparison page (/cards) works via GET /api/cards but direct API access fails

This issue was created by sre-sre-three-rivers-bswqe--b2b14894
Tracked by the SRE agent here

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions