Skip to Content
IssuesAPI Logs: All Responses Show statusCode: null

API Logs: All Responses Show statusCode: null

Status: ✅ Fixed (2026-05-05) — custom res serializer added to packages/logger/src/logger.ts
File: packages/logger/src/logger.tscreateLogger() serializers

Symptom

Every single API response in logs/api.log shows "statusCode": null, regardless of the actual HTTP response code:

[09:00:01] INFO [api] request completed res: { "statusCode": null, "headers": { ... }, "content-type": "application/json; charset=utf-8", "content-length": "767" } responseTime: 601.83

This affects every route — auth, tenant, admin — including both successful and potentially error responses. You cannot tell a 200 from a 400 or 500 from the logs alone.

Root Cause (Confirmed)

Three-component chain:

  1. Fastify v4 passes its Reply wrapper (not reply.raw) to the logger in onResponse:

    reply.log.info({ res: reply, responseTime }, 'request completed')
  2. pino-std-serializers@7.1.0 conditionally sets statusCode = null:

    function resSerializer(res) { _res.statusCode = res.headersSent ? res.statusCode : null // ... }
  3. Fastify’s Reply wrapper does not expose headersSent — only reply.raw (the underlying http.ServerResponse) has it. So res.headersSent reads as undefined (falsy) → statusCode = null for every single response.

Fix Applied

Replaced pino.stdSerializers.res with a custom serializer in packages/logger/src/logger.ts that resolves headersSent from reply.raw when not present on the outer object:

res(reply: any) { // FastifyReply doesn't expose headersSent; fall back to reply.raw. const raw = reply.raw ?? reply; const headersSent = raw.headersSent ?? true; return { statusCode: headersSent ? (reply.statusCode ?? raw.statusCode ?? null) : null, headers: typeof reply.getHeaders === "function" ? reply.getHeaders() : typeof raw.getHeaders === "function" ? raw.getHeaders() : {}, }; },

Two regression tests added to packages/logger/src/__tests__/logger.test.ts covering Fastify Reply and raw http.ServerResponse input shapes.

Impact

  • All API responses now log the correct HTTP status code
  • APM / log aggregation (e.g. Loki) can now alert on 5xx rates
  • Auth failures (401) are distinguishable from successes (200) in logs

© 2026 Leadmetrics — Internal use only