fix: return a real 404 for nonexistent athlete pages#257
Merged
Conversation
A loading.tsx at app/src/app/athlete/[slug]/ created an implicit Suspense boundary, which made Next stream the shell with a 200 status before the page's notFound() ran. So a nonexistent athlete returned HTTP 200 with the not-found UI — a soft 404 that search engines can index as a real page. Remove the segment's loading.tsx so Next resolves the page (and notFound()) before flushing the status. Missing athletes now return a real 404; existing athletes still return 200. The edge shard fetch is fast, so the lost loading skeleton costs little. Add e2e/athlete-404.spec.ts as a regression guard (verified red with the loading.tsx present, green without). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DaVB6YHWz94LTY8AGVc88Q
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
rootulp
enabled auto-merge (squash)
July 16, 2026 07:17
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
💤 Files with no reviewable changes (1)
📝 WalkthroughWalkthroughThe athlete route removes its loading UI to prevent a soft 404, documents the rendering constraint, and adds Playwright coverage for missing and existing athlete responses. ChangesAthlete route status handling
Estimated code review effort: 1 (Trivial) | ~5 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
rootulp
added a commit
that referenced
this pull request
Jul 16, 2026
* feat: add course grouping helpers to races manifest layer * feat: point course links to the new course detail page * refactor: extract pure computeRaceStats from getRaceStats * feat: add course result pooling and combined leaderboards * feat: add per-edition course summary roll-up * test: cover finishSeconds filter and second edition in course summary * feat: add course detail page aggregating all editions * fix: don't emit results metadata for an invalid course ?year * fix: return a real 404 for nonexistent course pages src/app/courses/loading.tsx created a Suspense boundary wrapping the [course] child segment, so Next streamed the shell with HTTP 200 before notFound() resolved (soft 404 for /courses/<bad-slug> and out-of-range ?year=). Moves the overview-only files (page.tsx, loading.tsx, and its chart components) into a (overview) route group so the loading boundary no longer wraps [course]. Same class of bug as /athlete/[slug], fixed in PR #257. Adds e2e/course-404.spec.ts mirroring athlete-404.spec.ts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A nonexistent athlete (e.g.
/athlete/no-such-person--zz-x) returned HTTP 200 with the not-found UI — a soft 404. Search engines can index soft 404s as real pages, and the status was misleading. Follow-up to #256.Root cause
app/src/app/athlete/[slug]/loading.tsxcreated an implicit Suspense boundary. That makes Next stream the shell with a 200 status before the page runs, so the page'snotFound()could only render the not-found UI — it couldn't change the already-flushed status. (Confirmed via response headers:HTTP/1.1 200 OK+Transfer-Encoding: chunkedfor a miss.)Fix
Remove the segment's
loading.tsxso Next resolves the page (andnotFound()) before flushing the status.Testing
e2e/athlete-404.spec.ts: asserts miss → 404 (+ not-found UI) and real → 200 (+ profile). Verified red withloading.tsxpresent, green without.Note: the same soft-404 pattern exists on
/race/[slug]/result/[id](also has aloading.tsx). Left out of this PR to keep it small — happy to fix it separately (its Node CSV-parse wait makes the loading skeleton more valuable, so it's a slightly different tradeoff).🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests