issue: reports are failing #49

Closed
opened 2026-03-18 13:13:15 +00:00 by cpfarhood · 3 comments
cpfarhood commented 2026-03-18 13:13:15 +00:00 (Migrated from github.com)

Reports are failing with
"Failed to load report data"

Reports are failing with "Failed to load report data"
ghost commented 2026-03-18 13:24:06 +00:00 (Migrated from github.com)

Root cause identified and fix submitted

The /api/reports/clients endpoint crashes with a 500 because the churn risk query passes a JavaScript Date object into Drizzle ORM's sql template literal in a HAVING clause. The postgres driver cannot serialize Date objects in this context — it throws:

The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received an instance of Date

The other 4 report endpoints (summary, revenue, appointments, services) work fine — only the clients endpoint has this issue.

Fix: PR #50 converts the Date to an ISO string and adds a ::timestamptz cast.

## Root cause identified and fix submitted The `/api/reports/clients` endpoint crashes with a 500 because the churn risk query passes a JavaScript `Date` object into Drizzle ORM's `sql` template literal in a `HAVING` clause. The `postgres` driver cannot serialize `Date` objects in this context — it throws: > The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received an instance of Date The other 4 report endpoints (summary, revenue, appointments, services) work fine — only the clients endpoint has this issue. **Fix:** PR #50 converts the `Date` to an ISO string and adds a `::timestamptz` cast.
ghost commented 2026-03-18 13:29:24 +00:00 (Migrated from github.com)

Investigation

I reviewed the reports code end-to-end (apps/api/src/routes/reports.ts, apps/web/src/pages/Reports.tsx, auth middleware, Drizzle schema, and migrations).

The frontend throws "Failed to load report data" when any of the 5 API endpoints (/summary, /revenue, /appointments, /services, /clients) returns a non-2xx response. The error gives no information about which one failed or why.

The API route handlers have no error handling — if any Drizzle query or DB call throws, Hono returns an empty 500 that the frontend treats as a failure. Without access to the pod logs or the browser network tab, the specific cause can't be determined from code alone.

Fix

PR #51 adds:

  1. reportsRouter.onError() — catches unhandled exceptions, logs them server-side, and returns a JSON { error, message } 500 so the cause is visible.
  2. Improved frontend error message — shows which specific endpoint failed plus the HTTP status and backend error message (e.g. clients (HTTP 500: relation "invoice_tip_splits" does not exist)).

Once this is deployed, the next time reports fail, the error in the UI will identify the root cause directly.

To diagnose now (before the PR deploys)

If you can run kubectl -n groombook logs -l app.kubernetes.io/name=api --tail=100, any DB or query errors will appear there as unhandled exceptions.

## Investigation I reviewed the reports code end-to-end (`apps/api/src/routes/reports.ts`, `apps/web/src/pages/Reports.tsx`, auth middleware, Drizzle schema, and migrations). The frontend throws "Failed to load report data" when any of the 5 API endpoints (`/summary`, `/revenue`, `/appointments`, `/services`, `/clients`) returns a non-2xx response. The error gives no information about which one failed or why. The API route handlers have no error handling — if any Drizzle query or DB call throws, Hono returns an empty 500 that the frontend treats as a failure. Without access to the pod logs or the browser network tab, the specific cause can't be determined from code alone. ## Fix PR #51 adds: 1. `reportsRouter.onError()` — catches unhandled exceptions, logs them server-side, and returns a JSON `{ error, message }` 500 so the cause is visible. 2. Improved frontend error message — shows which specific endpoint failed plus the HTTP status and backend error message (e.g. `clients (HTTP 500: relation "invoice_tip_splits" does not exist)`). Once this is deployed, the next time reports fail, the error in the UI will identify the root cause directly. ## To diagnose now (before the PR deploys) If you can run `kubectl -n groombook logs -l app.kubernetes.io/name=api --tail=100`, any DB or query errors will appear there as unhandled exceptions.
ghost commented 2026-03-18 13:36:43 +00:00 (Migrated from github.com)

Resolved — root cause confirmed and merged in PR #51.

Bug: The /api/reports/clients endpoint crashed with a 500 on every load. A raw JavaScript Date object was embedded in a sql template literal inside .having() in the churn-risk query. postgres-js cannot serialize a Date in this position — it threw a type error, the route handler crashed, and Hono returned an empty 500.

Fix: Serialize the date as an ISO string with ::timestamptz cast so PostgreSQL handles it correctly. Also added reportsRouter.onError() and improved the frontend error message for easier diagnosis of any future regressions.

**Resolved** — root cause confirmed and merged in PR #51. **Bug**: The `/api/reports/clients` endpoint crashed with a 500 on every load. A raw JavaScript `Date` object was embedded in a `sql` template literal inside `.having()` in the churn-risk query. `postgres-js` cannot serialize a `Date` in this position — it threw a type error, the route handler crashed, and Hono returned an empty 500. **Fix**: Serialize the date as an ISO string with `::timestamptz` cast so PostgreSQL handles it correctly. Also added `reportsRouter.onError()` and improved the frontend error message for easier diagnosis of any future regressions.
This repo is archived. You cannot comment on issues.
1 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: groombook/app#49