Skip to content

Profile and community pages answer 404 when the account or community lookup fails #2000

Closed

Description

@feruzm

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 404 for 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

  1. A prefetchRequired(options) helper next to prefetchQuery, 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-unavailable and throws a typed SsrLookupUnavailableError.
      It runs through the same React Query fetch as today, so the layout's prefetch and the page's still share one request.
  2. ssr-degraded.js: a response marked lookup-unavailable whose render ended in a server error goes out as 503 with Retry-After, still private, no-store. A 404 or 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.
  3. An error.tsx for the profile/[username] and community/[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).
  4. 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 SsrLookupUnavailableError and does not call notFound(). 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 calls notFound(). Spec: apps/web/src/specs/app/profile-community-lookup-unavailable.spec.ts
  • Given a lookup that errors or times out, when prefetchRequired runs, then it marks lookup-unavailable with the error and rejects with SsrLookupUnavailableError; given data it returns it; given null or a not-found assert it calls notFound() and marks nothing. Spec: apps/web/src/specs/core/react-query/prefetch-required.spec.ts
  • Given a response marked lookup-unavailable that ends with status 500, when its head is written, then it goes out as 503 with Retry-After and private, 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 SsrLookupUnavailableError event, when the server Sentry beforeSend runs, 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

  1. feruzm commented on Oct 30, 2026

    @feruzm
    MemberAuthor

    Scope note: the wave page (waves/[author]/[permlink]) had the same pattern (a failed post lookup answered notFound()), 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.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions