---
title: Fix players-view CSV export RangeError with bounded chunked rendering
type: task
status: completed
description: >-
  The players-view CSV export throws RangeError: Invalid string length because convert_to_csv
  accumulates the whole file in one string and the export resolves matchup cells by stringifying the
  entire player map. Resolve cells like the row renderer and emit bounded chunks straight into the
  Blob.
base_uri: user:task/league/fix-players-view-csv-export-string-length.md
created_at: '2026-10-19T16:17:03.136Z'
entity_id: 765590c7-d2b8-4db4-b8b9-cdef9b3847ef
finished_at: '2026-10-19T16:54:36.632Z'
observations:
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
started_at: '2026-10-19T16:39:38.598Z'
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-10-19T16:54:36.632Z'
visibility_analyzed_at: '2026-11-05T23:49:26.518Z'
---

## Background

Signal 136856 (`log_error`, `league-client`, opened 2026-10-09) reports `RangeError: Invalid string length` from the players-view CSV export:

```
at render_row (libs-shared/convert-to-csv.mjs:105:11)
at _ (app/core/export/csv.js:11:29)
at onClick (app/views/pages/players/players.js:163:8)
```

Two conditions compose:

- `convert_to_csv` appends every rendered row to one JavaScript string with no bound, so an export larger than V8's maximum string length throws.
- `app/views/pages/players/players.js` resolves each export cell with `player_map.getIn(field_info.key_path)` and ignores `get_player_field_value`. Matchup fields (`opponent`, `opponent_strength`, ...) carry an empty `key_path`, and `getIn([])` returns the whole player map, so the renderer JSON-stringifies an entire player into every such cell. The players store holds roughly 121,000 players, which is what makes the output exceed the limit.

The row renderer at `app/views/components/player-row/player-row.js:64` already prefers `get_player_field_value`; the export path must use the same precedence.

## Plan

- [x] Resolve export cell values with the row renderer's precedence: `get_player_field_value(player_map)` when present, otherwise `getIn(key_path)`.
- [x] Add bounded chunked rendering to `convert_to_csv` and pass the chunk array directly to the `Blob` so no single string holds the whole file.
- [x] Add specs for chunk bounds and field-value resolution; run `test/common.convert-to-csv.spec.mjs` and the related app spec.
- [x] Commit in `repository/active/league` and close this task with a completion observation.

## Deploy

The operator authorized the deploy on 2026-10-19.

- Range: 9 commits between the deployed sha and `origin/master`, including `11c61c701`.
- Preflight at 16:36Z refused because `HEAD` was 9 commits ahead of `origin/master`. `████████` holds the league push under the carve-out debounce (quiet window 10 min, oldest-unpushed ceiling 30 min; oldest was about 19 min at 16:35Z).
- Deploy claimed with the live league siblings: `██████████████████████████`, `█████████████████████████`, `████████████████████████`.
- `scripts/.tmp-fmt-2002.mjs` is an untracked sibling temp file in the shared league tree. It blocks the frontend tree check and is not mine to delete; requested its owner remove it.
- Range resolved at 27 commits from deployed `c49cd31be` to `origin/master` `e6976a925`, including four private pointer bumps. No league had a live free-agency window, so a process-wide reload was safe.
- A sibling session (`deploy-judged`) began `DEPLOY_ACK_RANGE=27 yarn deploy:all` from a worktree at `e6976a925` before this session started its own deploy. That tree includes `11c61c701`, so its bundle ships this fix. This session did not run a concurrent deploy.
- Verified: both hosts at `e6976a925`, `dist/build-manifest.json` names `e6976a925`, and the served `/dist/main.fbb02c94.js` (referenced by `https://xo.football/`) contains `as_parts`. This session's pinned worktree was removed.
