Repository navigation
Description
Problem
When a server-side lookup of an account or community fails for a transient reason (an RPC node error, rate limit or timeout), the profile and community pages answer 404 with the "Lost in ocean" page, as if the account or community did not exist. Reloading usually works, so users report pages that "sometimes 404".
The failure and the absence look the same to the page: prefetchQuery resolves undefined for a failed lookup while every page under profile/[username] and community/[community] treats a falsy result as notFound(). The response is already kept out of shared caches (ssr-degraded.js sends it private, no-store), but the status stays 404:
- The reader is told the account does not exist and gets no retry.
- Crawlers receive a hard
404for a live profile, which is a removal signal rather than a temporary one. - The edge retry to another origin and the origin's stale-copy fallback only engage on a
5xx, so neither can step in.
On a client-side navigation the same failure shows the not-found screen inside the app.
Related symptom from user reports: when a server render does throw, the only error module is global-error.tsx, a full-page "500" with no retry.
Proposal
- A
prefetchRequired(options)helper next toprefetchQuery, used by the pages that need the record to render:- returns the data when the lookup succeeded,
- calls
notFound()only when the lookup succeeded and found nothing, or a node answered with a not-found assert (isHiveNotFoundError), - otherwise (error, timeout) marks the response
lookup-unavailableand throws a typedSsrLookupUnavailableError.
It runs through the same React Query fetch as today, so the layout's prefetch and the page's still share one request.
ssr-degraded.js: a response markedlookup-unavailablewhose render ended in a server error goes out as503withRetry-After, stillprivate, no-store. A404or a streamed response is left alone. The per-minute log line carries a short sample of the failing error per reason, so a failing upstream is identifiable from the container log.- An
error.tsxfor theprofile/[username]andcommunity/[community]segments. It catches the typed failure on client-side navigation and offers a retry inside the page chrome instead of the global 500. It reports to Sentry only errors that did not come from the server render (those are already captured server-side). - The server Sentry config drops
SsrLookupUnavailableError: it is an expected, already-logged upstream condition, not a code defect.
Pages to switch from if (!x) notFound() to prefetchRequired: the profile index, [section] (both branches), followers, following, trail, communities, permissions, settings, referrals, insights; the community index, [tag] (both branches), activities, subscribers, roles.
Acceptance tests
- Given an account lookup whose RPC call fails, when a profile page renders on the server, then it throws
SsrLookupUnavailableErrorand does not callnotFound(). Spec:apps/web/src/specs/app/profile-community-lookup-unavailable.spec.ts - Given an account that does not exist (empty result), when a profile page renders, then it calls
notFound(). Spec:apps/web/src/specs/app/profile-community-lookup-unavailable.spec.ts - Given a community lookup that fails, when a community page renders, then it throws
SsrLookupUnavailableError; given a not-found assert, then it callsnotFound(). Spec:apps/web/src/specs/app/profile-community-lookup-unavailable.spec.ts - Given a lookup that errors or times out, when
prefetchRequiredruns, then it markslookup-unavailablewith the error and rejects withSsrLookupUnavailableError; given data it returns it; givennullor a not-found assert it callsnotFound()and marks nothing. Spec:apps/web/src/specs/core/react-query/prefetch-required.spec.ts - Given a response marked
lookup-unavailablethat ends with status 500, when its head is written, then it goes out as 503 withRetry-Afterandprivate, no-store; a marked 404 keeps its 404; the minute log line includes the error sample. Spec:apps/web/src/specs/ssr-degraded.spec.ts - Given the profile or community segment error boundary, when it shows a server-render error, then it offers a retry that refreshes the route and does not report to Sentry; given a client-side error, then it reports it. Spec:
apps/web/src/specs/app/lookup-unavailable-error-boundary.spec.tsx - Given a
SsrLookupUnavailableErrorevent, when the server SentrybeforeSendruns, then it is dropped; other errors pass. Spec:apps/web/src/specs/utils/sentry-config-wiring.spec.ts
Out of scope
- Post pages: a failed lookup there already renders the client-side fallback that re-reads the chain from the browser, not a 404.
- RPC node selection and ordering: the SDK already ranks nodes by latency and parks throttling ones. The new log sample shows whether a node change is needed.
Activity
feruzm commentedon Oct 30, 2026
Scope note: the wave page (
waves/[author]/[permlink]) had the same pattern (a failed post lookup answerednotFound()), so it is covered in #2001 as well. Account names no account can have (too long, invalid characters) are classified as not found, so those URLs stay cacheable 404s.