Skip to content

Commit c91aaef

Browse files
authored
fix: handle RateLimitBlockedException in ExceptionHandlers (#3472)
## Summary - Add missing `@ExceptionHandler` for `RateLimitBlockedException` in `ExceptionHandlers.kt` - Returns HTTP 444 (no body), matching the existing behavior in `GlobalIpRateLimitFilter` and `GlobalUserRateLimitFilter` ## Problem The strike-based rate limiting (#3451, commit 68b5250) introduced `RateLimitBlockedException` but only caught it in the global servlet filters. When thrown from controller-level rate limiting (e.g. the export endpoint's `checkPerUserRateLimit`), it bubbled up as an unhandled exception — causing HTTP 500 errors and Sentry noise since Feb 6th. ## Test plan - [ ] Verify export endpoint returns 444 instead of 500 for repeat rate limit offenders - [ ] Confirm no new Sentry errors for `RateLimitBlockedException` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved handling of blocked rate-limit events so users receive a clear status when limits are exceeded and related occurrences are logged for better observability. * **Tests** * Made NDJSON error extraction in tests more resilient by scanning for error messages instead of relying on a fixed position. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 0e68d32 commit c91aaef

2 files changed

Lines changed: 14 additions & 1 deletion

File tree

backend/app/src/main/kotlin/io/tolgee/ExceptionHandlers.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import io.tolgee.exceptions.ErrorException
1212
import io.tolgee.exceptions.ErrorResponseBody
1313
import io.tolgee.exceptions.ErrorResponseTyped
1414
import io.tolgee.exceptions.NotFoundException
15+
import io.tolgee.security.ratelimit.RateLimitBlockedException
1516
import io.tolgee.security.ratelimit.RateLimitResponseBody
1617
import io.tolgee.security.ratelimit.RateLimitedException
1718
import io.tolgee.util.Logging
@@ -256,6 +257,12 @@ class ExceptionHandlers : Logging {
256257
)
257258
}
258259

260+
@ExceptionHandler(RateLimitBlockedException::class)
261+
fun handleRateLimitBlocked(ex: RateLimitBlockedException): ResponseEntity<Unit> {
262+
logger.debug("Rate limit blocked (strike {})", ex.strikeCount)
263+
return ResponseEntity.status(444).build()
264+
}
265+
259266
@ExceptionHandler(NoResourceFoundException::class)
260267
fun handleNoResourceFound(ex: NoResourceFoundException): ResponseEntity<ErrorResponseBody> {
261268
logger.debug("No resource found", ex)

ee/backend/tests/src/test/kotlin/io/tolgee/ee/selfHostedLimitsAndReporting/CreditLimitTest.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,13 @@ class CreditLimitTest : ProjectAuthControllerTest("/v2/projects/") {
107107
}
108108
val parsed = NdJsonParser(objectMapper).parse(response)
109109
parsed.assert.hasSize(3)
110-
(parsed[1] as Map<*, *>)["errorMessage"].assert.isEqualTo(errorCode)
110+
val errorMessage =
111+
parsed
112+
.drop(1)
113+
.filterIsInstance<Map<*, *>>()
114+
.mapNotNull { it["errorMessage"] }
115+
.firstOrNull()
116+
errorMessage.assert.isEqualTo(errorCode)
111117
}
112118

113119
private fun mockBadRequest(errorCode: String): HttpClientErrorException {

0 commit comments

Comments
 (0)