Skip to content

fix(#358): team route degrades gracefully on data-store failure - #359

Open
ChesterSchendel wants to merge 1 commit into
mainfrom
claude/358-team-fetch-resilience
Open

fix(#358): team route degrades gracefully on data-store failure#359
ChesterSchendel wants to merge 1 commit into
mainfrom
claude/358-team-fetch-resilience

Conversation

@ChesterSchendel

Copy link
Copy Markdown
Contributor

What & why

Follow-up hardening to today's prod incident where /dashboard/team returned Team fetch failed (500) after the Vercel KV migration. Root cause was infra (stale UPSTASH_* vars in Production overrode the new KV_REST_API_*, so Redis.fromEnv() read a dead store) and has already been fixed in Vercel + the data restored. This PR addresses the code defect that made the failure opaque and total.

The handler had no error handling. Any throw in the Redis-backed enrichment became a bare 500 with no server-side log — which is why diagnosing it took reverse-engineering instead of reading a stack trace. It also explained the split symptom: managers run per-member Redis reads (→ 500), viewers make no Redis calls (→ page rendered).

Changes

  • GET is now a thin wrapper around buildTeamResponse() with an outer try/catch that logs the real error (console.error("[dashboard/team] fetch failed", err)).
  • Graceful degrade: per-member and archived enrichment failures fall back to roster-only (stats: null) and flip degraded: true, instead of throwing the whole request. The roster comes from Clerk, so it renders even when the data store is down. Only genuine auth/Clerk failures return 500.
  • Response carries degraded: boolean.
  • Client renders a calm, non-blocking notice when degraded ("Live performance data is temporarily unavailable… roster is current and scoring still works") instead of the red 500 banner.

Net effect

Today's incident would have shown the team roster with a notice and a logged root cause, rather than a blank red error.

/hq lockstep

  • /hq/api: documented the team endpoint's degraded behavior + stamped frontmatter (lastPr 359).

Verification

  • tsc --noEmit: no new errors (the lone validator.ts line is a pre-existing stale .next artifact).
  • eslint: clean.
  • Degrade path isn't browser-verifiable without an authed manager session + induced Redis failure; logic verified by inspection.

Resolves #358.

🤖 Generated with Claude Code

The /dashboard/team GET handler had no error handling, so any throw in the
Redis-backed enrichment surfaced as an opaque 500 — exactly what happened
when prod's Redis.fromEnv() resolved to a stale/empty store after the
KV migration. Managers (who run per-member Redis reads) 500'd; viewers
(no Redis calls) saw the page. The failure was invisible: no server-side log.

- Wrap GET in a thin wrapper around buildTeamResponse() that logs the real
  error instead of returning a bare 500.
- Degrade gracefully: per-member and archived enrichment failures fall back
  to roster-only (stats: null) and set `degraded: true` rather than failing
  the whole request. Only auth/Clerk failures now return 500.
- Client shows a non-blocking notice when `degraded`, instead of the red
  "Team fetch failed (500)" error.
- /hq/api: document the new degraded behavior + stamp.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
runladder Ready Ready Preview, Comment Jun 24, 2026 10:54pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prod: team scores/usage gone + /dashboard/team 500s for managers after app.runladder.com migration

1 participant