---
title: 'Harden league-client: reducer projections guard + data_view_id 4xx reporting'
type: task
status: completed
description: >-
  Two emit-only league-client log_errors (#116459 unguarded GET_PLAYER_PROJECTIONS_FULFILLED forEach
  on undefined payload.data; #116460 stale/malformed saved data_view_id surfaced as a log_error
  though server 400 validation is correct) need a client bundle rebuild to fix. Tracked to land with
  the maintainer's active data-views work rather than a competing push.
base_uri: user:task/league/harden-client-reducer-guard-and-dataview-id-reporting.md
created_at: '2026-07-01T15:09:34.814Z'
entity_id: 82a5524f-b131-4c6b-83d2-399acd9ee826
finished_at: '2026-07-01T15:33:11.200Z'
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-07-01T15:33:11.200Z'
visibility_analyzed_at: '2026-11-05T23:49:35.648Z'
---

## Context

Two emit-only `league-client` `log_error` signals from 2026-06-30 21:06 remain after
the DB-role-rename cluster was resolved. Both require a **client bundle rebuild**
(`yarn build && yarn deploy:dist && yarn deploy:sourcemaps`) to reach production, so
they are tracked rather than hot-fixed — and the reducer file is in the middle of the
maintainer's active data-views work (`332bced0`, `86dd00e7`, `71b0bc90` all 2026-06-30),
so these should land alongside that work, not as a competing push.

### Signal #116459 — reducer forEach on undefined

`TypeError: Cannot read properties of undefined (reading 'forEach')`, reported at
`app/core/players/reducer.js:591` (symbolicated against the private sourcemap; the
line maps approximately — current source has shifted). Fired 2026-06-30 21:06, ~3h
BEFORE the pg_hba DB outage (which started 2026-07-01 00:00), so it was a transient
bad-payload event, not the DB outage.

Decisive read of the mechanism: the error text "reading 'forEach'" of `undefined`
means the receiver itself is `undefined`. The `DATA_VIEW_RESULT` case is already
guarded — `(payload.result || []).forEach(...)` (commit `332bced0`, confirmed live
in the deployed bundle `main.045bd39e.js` as minified `(t.result||[]`), and `|| []`
can never be undefined — so that is NOT the crash site. The remaining UNGUARDED site
is `GET_PLAYER_PROJECTIONS_FULFILLED`:

```
const projections = payload.data
...
projections.forEach((projection) => { ... })   // throws when payload.data is undefined
```

When the projections fetch resolves with a body lacking `data` (API error/empty),
`payload.data` is undefined and `.forEach` throws.

### Signal #116460 — invalid data_view_id (malformed saved id)

`Error: invalid data_view_id` thrown by `app/core/api/service.js` `dispatch_fetch`
(surfaces any non-2xx) for `GET /api/data-views/tbd53b58bef262876963a62f5d5ce2672`.
The server (`api/routes/data-views.mjs:627`) correctly returns 400 — real `view_id`s
are 36-char UUIDs (verified against `user_data_views`), while this id is 33 chars, no
dashes, leading `tb`, matching no row. So this is a stale/corrupt client saved-view
reference, NOT a server validation gap; server validation works. The residual issue
is client-error-reporting noise: an expected 4xx is emitted as a `log_error`.

## Plan

- [ ] #116459: guard `GET_PLAYER_PROJECTIONS_FULFILLED` in `app/core/players/reducer.js`
      — `const projections = payload.data || []` (and mirror any other unguarded
      `payload.*.forEach` in the reducer). Follows the `332bced0` `|| []` pattern.
- [ ] #116460: prefer NOT emitting a `log_error` for expected client 4xx like
      `invalid data_view_id` (align with the existing client-error filter work —
      `fc7b140c`, `95b645df`, and `user:task/league/deploy-verify-client-error-crawler-filter.md`).
      Optionally also validate `view_id` is a UUID before dispatching the GET, and/or
      clear the corrupt saved view for the affected user so the client stops requesting it.
- [ ] Rebuild + deploy the client bundle: `yarn build && yarn deploy:dist && yarn deploy:sourcemaps`
      (a `dist/` rebuild without matching sourcemaps degrades future symbolication).
- [ ] Confirm neither `log_error` fingerprint re-emits after deploy.

## Key locations

- `repository/active/league/app/core/players/reducer.js` (`GET_PLAYER_PROJECTIONS_FULFILLED` forEach)
- `repository/active/league/app/core/api/service.js` (`dispatch_fetch` non-2xx throw)
- `repository/active/league/api/routes/data-views.mjs:614-635` (server 400 validation — working as intended)
- Client error reporting / filter: `api/routes/errors.mjs`, `user:task/league/deploy-verify-client-error-crawler-filter.md`
