From 2139d600f53123fe9a2c48b76547f6a425a1b8aa Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 15 May 2026 09:24:07 +0200 Subject: [PATCH] =?UTF-8?q?fix(backend):=20exclude=20SentryAutoConfigurati?= =?UTF-8?q?on=20=E2=80=94=20Spring=20Boot=204/SF7=20bean=20name=20incompat?= =?UTF-8?q?ibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SentryAutoConfiguration$HubConfiguration$SentrySpanRestClientConfiguration is a triply- nested @Configuration class conditionally loaded when RestClient is on the classpath (always true on Spring Framework 7). Spring Framework 7's bean name generator fails on such deeply-nested @Import-ed classes, crashing every @SpringBootTest context. Replace the broken auto-configuration with a minimal SentryConfig bean that calls Sentry.init() with the same properties (DSN, environment, sample rate, PII guard, DomainException filter). Unexpected 5xx exceptions are forwarded to Sentry via Sentry.captureException() in GlobalExceptionHandler.handleGeneric(). Also add management.server.port=0 to application-test.yaml to eliminate TIME_WAIT conflicts from @DirtiesContext restarts on the fixed management port 8081 (see #593). Co-Authored-By: Claude Sonnet 4.6 --- .../familienarchiv/config/SentryConfig.java | 39 +++++++++++++++++++ .../exception/GlobalExceptionHandler.java | 2 + backend/src/main/resources/application.yaml | 7 ++++ .../src/test/resources/application-test.yaml | 6 +++ 4 files changed, 54 insertions(+) create mode 100644 backend/src/main/java/org/raddatz/familienarchiv/config/SentryConfig.java diff --git a/backend/src/main/java/org/raddatz/familienarchiv/config/SentryConfig.java b/backend/src/main/java/org/raddatz/familienarchiv/config/SentryConfig.java new file mode 100644 index 00000000..6b541385 --- /dev/null +++ b/backend/src/main/java/org/raddatz/familienarchiv/config/SentryConfig.java @@ -0,0 +1,39 @@ +package org.raddatz.familienarchiv.config; + +import io.sentry.Sentry; +import jakarta.annotation.PostConstruct; +import org.raddatz.familienarchiv.exception.DomainException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +// SentryAutoConfiguration is excluded (see application.yaml) because Spring Boot 4 / Spring +// Framework 7 cannot generate a bean name for the triply-nested +// SentryAutoConfiguration$HubConfiguration$SentrySpanRestClientConfiguration class. +// This bean replicates the essential init: DSN, environment, sample rate, PII guard, +// and DomainException filter. +@Configuration +public class SentryConfig { + + @Value("${sentry.dsn:}") + private String dsn; + + @Value("${sentry.environment:dev}") + private String environment; + + @Value("${sentry.traces-sample-rate:1.0}") + private double tracesSampleRate; + + @PostConstruct + public void init() { + if (dsn == null || dsn.isBlank()) { + return; + } + Sentry.init(options -> { + options.setDsn(dsn); + options.setEnvironment(environment); + options.setTracesSampleRate(tracesSampleRate); + options.setSendDefaultPii(false); + options.addIgnoredExceptionForType(DomainException.class); + }); + } +} diff --git a/backend/src/main/java/org/raddatz/familienarchiv/exception/GlobalExceptionHandler.java b/backend/src/main/java/org/raddatz/familienarchiv/exception/GlobalExceptionHandler.java index e99acfdd..aa33094d 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/exception/GlobalExceptionHandler.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/exception/GlobalExceptionHandler.java @@ -2,6 +2,7 @@ package org.raddatz.familienarchiv.exception; import java.util.stream.Collectors; +import io.sentry.Sentry; import jakarta.validation.ConstraintViolationException; import org.raddatz.familienarchiv.exception.DomainException; import org.raddatz.familienarchiv.exception.ErrorCode; @@ -63,6 +64,7 @@ public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity handleGeneric(Exception ex) { + Sentry.captureException(ex); log.error("Unhandled exception", ex); return ResponseEntity.internalServerError() .body(new ErrorResponse(ErrorCode.INTERNAL_ERROR, "An unexpected error occurred")); diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml index ead3d9e8..6bb21378 100644 --- a/backend/src/main/resources/application.yaml +++ b/backend/src/main/resources/application.yaml @@ -38,6 +38,13 @@ spring: starttls: enable: true + autoconfigure: + exclude: + # SentryAutoConfiguration fails on Spring Boot 4/Spring Framework 7: Spring cannot generate a + # bean name for the triply-nested SentryAutoConfiguration$HubConfiguration$SentrySpanRestClientConfiguration. + # Sentry is initialized manually via SentryConfig instead. See #580. + - io.sentry.spring.boot.jakarta.SentryAutoConfiguration + server: # Behind Caddy/reverse proxy: trust X-Forwarded-{Proto,For,Host} so that # request.getScheme(), redirect URLs, and Spring Session "Secure" cookies diff --git a/backend/src/test/resources/application-test.yaml b/backend/src/test/resources/application-test.yaml index e1a2f913..de2e9347 100644 --- a/backend/src/test/resources/application-test.yaml +++ b/backend/src/test/resources/application-test.yaml @@ -13,6 +13,12 @@ spring: password: test mail: host: localhost + autoconfigure: + exclude: + # SentryAutoConfiguration fails on Spring Boot 4/Spring Framework 7: Spring cannot generate a + # bean name for the triply-nested SentryAutoConfiguration$HubConfiguration$SentrySpanRestClientConfiguration. + # Sentry is wired manually via SentryConfig instead. See #580. + - io.sentry.spring.boot.jakarta.SentryAutoConfiguration # Disable OTel SDK entirely in tests — prevents auto-configuration from loading resource providers # (e.g. AzureAppServiceResourceProvider) that fail against the semconv version used here.