From 958ee2548c79d96073b516ce69aec5eac3257b8d Mon Sep 17 00:00:00 2001 From: Gift Amadi <120387225+giftexceed@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:58:49 +0000 Subject: [PATCH] fix(listener): exempt rate-limit metrics endpoint from rate limiting GET /api/rate-limit/metrics was subject to the rate limiter itself, so a client that had already exhausted its quota received 429 when trying to read the metrics. Parse the request URL before the limiter check and skip limiting for the metrics observability route. Fixes the failing "provides rate limiting metrics via GET /api/rate-limit/metrics" test in rate-limiter.test.ts, which made 3 events requests (tripping the limit) and then expected the metrics endpoint to return 200. --- listener/src/api/events-server.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/listener/src/api/events-server.ts b/listener/src/api/events-server.ts index 197e189e..75331769 100644 --- a/listener/src/api/events-server.ts +++ b/listener/src/api/events-server.ts @@ -156,7 +156,15 @@ export function createEventsServer(options: EventsServerOptions): http.Server { res.setHeader('X-Request-Id', requestId); res.setHeader('X-Correlation-Id', correlationId); - if (rateLimiter) { + const url = new URL(req.url ?? '/', 'http://localhost'); + + // The rate-limit metrics endpoint is an observability route and must stay + // reachable even after a client exhausts its quota — otherwise callers + // can't read the very metrics that explain why they are being throttled. + const isRateLimitExempt = + req.method === 'GET' && url.pathname === '/api/rate-limit/metrics'; + + if (rateLimiter && !isRateLimitExempt) { const allowed = await rateLimiter.handle(req, res as any); if (!allowed) return; } @@ -167,8 +175,6 @@ export function createEventsServer(options: EventsServerOptions): http.Server { return; } - const url = new URL(req.url ?? '/', 'http://localhost'); - // GET /health if (req.method === 'GET' && url.pathname === '/health') { buildHealthResponse(options).then((health) => {