Bug diagnosis protocol for Spring Boot applications including reproduce-first workflow, stack trace analysis, and common bug patterns.
npx claudepluginhub sumanpapanaboina1983/adlc-accelerator-kit-pluginsThis skill uses the workspace's default tool permissions.
| Gate | Threshold | Status |
Runs Spring Boot verification pipeline: build, static analysis, tests with coverage, security scans before PRs, changes, or deployment.
Runs verification loop for Spring Boot projects: build with Maven/Gradle, static analysis (SpotBugs/PMD/Checkstyle), tests with Jacoco coverage, security scans, and diff review before PRs or releases.
Provides Spring Boot mastery covering auto-configuration, security, Data JPA, Actuator, and TestContainers testing for Java/Kotlin backends.
Share bugs, ideas, or general feedback.
| Gate | Threshold | Status |
|---|---|---|
| Compile | 0 errors | REQUIRED |
| Tests | 100% pass (0 failures) | REQUIRED |
| Coverage | ≥ 80% line coverage | REQUIRED |
| Checkstyle | 0 violations | REQUIRED (if configured) |
⛔ BLOCKING: Bug fix is NOT complete until ALL gates pass.
Before fixing ANY bug, verify JaCoCo is configured:
grep -l "jacoco-maven-plugin" pom.xml
If NOT configured, add JaCoCo plugin to pom.xml first (see springboot-testing skill for configuration).
┌─────────────────────────────────────────────────────────────┐
│ STEP 0: VERIFY COVERAGE CONFIGURATION │
│ - Check: grep "jacoco-maven-plugin" pom.xml │
│ - If missing → Configure JaCoCo FIRST │
│ - ⛔ Do NOT proceed without coverage configuration │
│ │
│ STEP 1: UNDERSTAND │
│ - Read bug report / error trace / Jira ticket │
│ - Identify: EXPECTED behavior vs ACTUAL behavior │
│ - Note: specific inputs, environment, frequency │
│ │
│ STEP 2: REPRODUCE │
│ - Write a test that exercises the exact bug scenario │
│ - Run the test → it MUST FAIL │
│ - If it passes → you haven't reproduced the bug yet │
│ - The failing test IS your proof the bug exists │
│ │
│ STEP 3: DIAGNOSE │
│ - Read stack trace → find originating line │
│ - Trace execution: Controller → Service → Repository │
│ - Classify: DATA / LOGIC / CONFIG / DEPENDENCY issue │
│ - Form hypothesis → verify → don't guess │
│ │
│ STEP 4: FIX │
│ - Fix the ROOT CAUSE (not the symptom) │
│ - Minimal change that addresses the actual problem │
│ │
│ STEP 5: VERIFY │
│ - Run reproducing test → must now PASS │
│ - Run FULL test suite → zero regressions │
│ - All existing tests must still pass │
│ │
│ STEP 6: COVERAGE VERIFICATION ⚠️ MANDATORY │
│ - Run: mvn test jacoco:report │
│ - Check coverage ≥ 80% │
│ - If < 80% → add more tests → repeat │
│ - ⛔ Do NOT mark complete if coverage < 80% │
└─────────────────────────────────────────────────────────────┘
org.springframework.web.util.NestedServletException: Request processing failed
at org.springframework.web.servlet.FrameworkServlet.processRequest(...)
at org.springframework.web.servlet.FrameworkServlet.doPost(...)
...
Caused by: java.lang.NullPointerException ← ACTUAL EXCEPTION
at com.example.TodoService.create(TodoService.java:42) ← ROOT CAUSE LINE
at com.example.TodoController.create(TodoController.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(...)
...
Read bottom-up: The "Caused by" chain shows the real exception. The first line of YOUR code (com.example.*) is where to start investigating.
sun.reflect.* - Reflectionorg.springframework.aop.* - AOP proxycom.sun.proxy.* - Dynamic proxyorg.springframework.cglib.* - CGLIB proxyjdk.internal.* - JDK internals| Exception | Likely Root Cause | Where to Look |
|---|---|---|
NullPointerException | Uninitialized field, missing null check, failed injection | The line number in stack trace |
IllegalArgumentException | Invalid input not validated at boundary | Method parameter validation |
IllegalStateException | Method called at wrong time, object in wrong state | Object lifecycle |
DataIntegrityViolationException | FK violation, unique constraint, NOT NULL on null | Database schema + data |
OptimisticLockingFailureException | Concurrent modification, stale data | Transaction boundaries |
TransactionSystemException | Nested transaction issue, rollback-only | @Transactional usage |
HttpMessageNotReadableException | JSON doesn't match DTO | Request body structure |
MethodArgumentNotValidException | Validation annotation failed | DTO validation rules |
Symptoms:
Diagnosis:
# Check recent data changes
git log --oneline src/main/resources/db/migration/
# Query related data
# (Use MCP or direct DB access)
SELECT * FROM todos WHERE id = 123;
Common causes:
Symptoms:
Diagnosis:
// Add temporary logging
log.debug("Input: {}, Output: {}", input, result);
// Check boundary conditions
// < vs <=, > vs >=
// AND vs OR
// null handling
Common causes:
Symptoms:
Diagnosis:
# Check active profile
grep -r "spring.profiles.active" src/
# Check environment variables
echo $DATABASE_URL
# Check config precedence
# application.yml < application-{profile}.yml < env vars
Common causes:
Symptoms:
Diagnosis:
# Check dependency tree
mvn dependency:tree -Dincludes=problematic-artifact
# Check for conflicts
mvn dependency:analyze
Common causes:
// BUG: Validation annotations ignored
@PostMapping
public Todo create(@RequestBody CreateTodoRequest request) { ... }
// FIX: Add @Valid
@PostMapping
public Todo create(@Valid @RequestBody CreateTodoRequest request) { ... }
Test to reproduce:
@Test
void should_return400_whenTitleIsBlank() {
CreateTodoRequest request = new CreateTodoRequest("");
// Without @Valid: returns 500 (ConstraintViolation at DB)
// With @Valid: returns 400 (validation at controller)
}
// BUG: Partial commit on failure
public void transfer(Long fromId, Long toId, BigDecimal amount) {
accountRepository.withdraw(fromId, amount); // Commits
accountRepository.deposit(toId, amount); // If fails, money lost!
}
// FIX: Add @Transactional
@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) { ... }
// BUG: N+1 queries (1 for orders + N for items)
public List<OrderDto> findAllOrders() {
List<Order> orders = orderRepository.findAll();
return orders.stream()
.map(o -> new OrderDto(o, o.getItems())) // Each getItems() = query
.toList();
}
// FIX: Eager fetch or JOIN query
public List<OrderDto> findAllOrders() {
List<Order> orders = orderRepository.findAllWithItems(); // JOIN FETCH
return orders.stream().map(this::toDto).toList();
}
// BUG: LocalDateTime loses timezone info
LocalDateTime created = LocalDateTime.now();
// Stored as server timezone, read in different timezone = wrong time
// FIX: Use Instant (UTC) or ZonedDateTime
Instant created = Instant.now();
# BUG: Too many connections
spring.datasource.hikari.maximum-pool-size: 20
# With 5 pods: 20 × 5 = 100, but max_connections = 100 → exhaustion
# FIX: Size for replicas
spring.datasource.hikari.maximum-pool-size: 16
# 16 × 5 = 80 < 100 ✓
// BUG: Check-then-act race condition
public void updateIfExists(Long id, UpdateRequest request) {
if (repository.existsById(id)) { // Thread A checks
// Thread B deletes here
repository.update(id, request); // Thread A fails
}
}
// FIX: Atomic operation or optimistic locking
@Transactional
public void updateIfExists(Long id, UpdateRequest request) {
Todo todo = repository.findById(id)
.orElseThrow(() -> new NotFoundException(id));
todo.update(request);
repository.save(todo);
}
@Test
void should_[expectedBehavior]_when[bugCondition]() {
// Given - the exact input that causes the bug
[setup that triggers the bug]
// When - the exact operation
[call the method/endpoint]
// Then - the EXPECTED behavior (what SHOULD happen)
[assertion that currently FAILS]
// Before fix: test FAILS (proves bug exists)
// After fix: test PASSES (proves bug is fixed)
}
@Test
void should_return400_whenTitleIsEmpty() {
// Given
CreateTodoRequest request = new CreateTodoRequest("");
// When
ResponseEntity<ErrorResponse> response = restTemplate.postForEntity(
"/api/v1/todos",
request,
ErrorResponse.class
);
// Then
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
// Before fix: was 500 INTERNAL_SERVER_ERROR
// After fix: is 400 BAD_REQUEST ✓
}