Fix simulate route crash and surface actual simulation errors #64

Merged
chrisp merged 1 commit from fix/simulate-route-error-handling into main 2026-06-01 06:47:31 +00:00
Owner

Problem

Running a simulation on MLB (and any sport where the readiness check fails) showed a confusing React Router 400 error instead of the real failure reason. The cause: the simulate route had only an `action` export. When the action threw instead of redirecting, React Router tried to render the route via GET to display the error — hit the missing loader — and replaced the real error with its own internal 400.

A secondary issue: the interim fix used `?simulationError=` query params, which had three problems identified in code review:

  • Stale errors re-appeared when pressing browser Back after fixing the issue
  • The param reflected arbitrary text into the UI (social engineering surface)
  • The styling didn't match the established `bg-destructive/15` error pattern

Changes

`admin.sports-seasons.$id.simulate.tsx` — strip the action; keep a loader-only redirect stub so bookmarked URLs degrade gracefully to the season page.

`admin.sports-seasons.$id.tsx` — add `intent="simulate"` to the action dispatcher (matching `delete`, `rescore`, `sync-standings`, `finalize-standings`). On success: redirect to expected-values (unchanged). On failure: return `{ simulateError: message }` displayed inline with the correct `bg-destructive/15` styling.

Test plan

  • Click Run Simulation on a season with a missing readiness input → error message appears inline below the description text, styled consistently with other errors on the page
  • Click Run Simulation on a fully-configured season → redirects to expected-values as before
  • Navigate directly to `/admin/sports-seasons//simulate` in browser → redirects to season page (no 400)
  • Run simulation, see error, fix the issue, run again successfully → no stale error message persists

🤖 Generated with Claude Code

## Problem Running a simulation on MLB (and any sport where the readiness check fails) showed a confusing React Router 400 error instead of the real failure reason. The cause: the simulate route had only an \`action\` export. When the action **threw** instead of redirecting, React Router tried to render the route via GET to display the error — hit the missing loader — and replaced the real error with its own internal 400. A secondary issue: the interim fix used \`?simulationError=\` query params, which had three problems identified in code review: - Stale errors re-appeared when pressing browser Back after fixing the issue - The param reflected arbitrary text into the UI (social engineering surface) - The styling didn't match the established \`bg-destructive/15\` error pattern ## Changes **\`admin.sports-seasons.\$id.simulate.tsx\`** — strip the action; keep a loader-only redirect stub so bookmarked URLs degrade gracefully to the season page. **\`admin.sports-seasons.\$id.tsx\`** — add \`intent="simulate"\` to the action dispatcher (matching \`delete\`, \`rescore\`, \`sync-standings\`, \`finalize-standings\`). On success: redirect to expected-values (unchanged). On failure: return \`{ simulateError: message }\` displayed inline with the correct \`bg-destructive/15\` styling. ## Test plan - [ ] Click Run Simulation on a season with a missing readiness input → error message appears inline below the description text, styled consistently with other errors on the page - [ ] Click Run Simulation on a fully-configured season → redirects to expected-values as before - [ ] Navigate directly to \`/admin/sports-seasons/<id>/simulate\` in browser → redirects to season page (no 400) - [ ] Run simulation, see error, fix the issue, run again successfully → no stale error message persists 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chrisp added 1 commit 2026-06-01 06:43:00 +00:00
Fix simulate route crash and surface actual error messages
All checks were successful
🚀 Deploy / 🧪 Test (pull_request) Successful in 2m24s
🚀 Deploy / ʦ🔍 Typecheck & Lint (pull_request) Successful in 1m19s
🚀 Deploy / 🐳 Build (pull_request) Has been skipped
🚀 Deploy / 🚀 Deploy (pull_request) Has been skipped
ba970cd571
When a simulation action threw (e.g. readiness check failure), React Router
tried to render the simulate route via GET to display the error — but the
route had no loader, so the real error was completely swallowed by React
Router's own 400 "no loader" message.

Four issues fixed:
- Move simulate into the intent-dispatch pattern on the season page (matching
  delete/rescore/sync-standings/finalize-standings), so errors surface via
  actionData instead of being lost
- Eliminate the ?simulationError= query param approach, which left stale error
  messages in browser history and reflected user-controlled text in the UI
- Match the existing bg-destructive/15 error styling used elsewhere on the page
- Strip the now-unused action from simulate.tsx; keep a loader-only redirect
  stub so any bookmarked URLs degrade gracefully to the season page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chrisp merged commit ad910d4f40 into main 2026-06-01 06:47:31 +00:00
chrisp deleted branch fix/simulate-route-error-handling 2026-06-01 06:47:31 +00:00
Sign in to join this conversation.
No description provided.