Profile and list pages incorrectly show 404/empty state when API fails

Problem

During production traffic testing with ~10% of hive.blog traffic redirected to staging, several pages incorrectly display 404 or "Nothing more to load" messages when the underlying API calls fail (503 errors). This creates a poor user experience since:

  1. Users see "Page not found" for valid users when the API is temporarily overloaded
  2. Users see "Nothing more to load" on post lists when posts exist but couldn't be fetched
  3. The client can't retry because it was given a false 404 or empty state

Affected Pages

1. Profile pages (/@{username}/*)

File: apps/blog/features/layouts/user-profile/profile-layout.tsx (lines 84-92)

if (
  !dynamicGlobalData ||
  !profileData ||
  !profileData.delegated_vesting_shares ||
  !profileData.received_vesting_shares ||
  !profileData.vesting_shares
) {
  return notFound();
}

Problem: When the API fails (503) during SSR prefetch, profileData is undefined. The current code calls notFound() which returns a 404 page. However, this doesn't distinguish between:

  • User genuinely doesn't exist (should show 404)
  • API failed temporarily (should show error state with retry)

2. Trending/tag post lists (/trending, /trending/my, etc.)

File: apps/blog/features/tags-pages/list-of-posts.tsx (lines 86-95)

{isFetchingNextPage && !!data && data.pages.length > 0 ? (
  <div>Loading...</div>
) : hasNextPage ? (
  t('user_profile.load_newer')
) : (
  t('user_profile.nothing_more_to_load')
)}

Problem: When initial query fails, hasNextPage is undefined (falsy) and data is undefined. This shows "Nothing more to load" even though no data was fetched.

Expected Behavior

Use React Query's isError state to distinguish API failures from actual empty states:

  1. API Error (isError: true): Show error message with retry button. Client-side retry often succeeds for flaky APIs.

  2. Successful but Empty (isError: false, data: null/[]): Show appropriate "not found" or "empty" message.

  3. Loading (isPending: true): Show loading indicator.

This pattern is already correctly implemented in posts-content.tsx line 80:

if (isError) return <NoDataError />;

Technical Context

  • React Query default retry is 3 times, so isError: true indicates persistent failure
  • SSR prefetch catches errors and continues (see layout.tsx lines 97-99), allowing client to retry via hydration
  • Existing NoDataError component can be reused for error states
  • Format validation (invalid username format) should still return 404 - only API failures need this fix

Acceptance Criteria

  • profile-layout.tsx shows error state with retry when profileData query fails
  • profile-layout.tsx shows 404 only when API confirms user doesn't exist
  • list-of-posts.tsx shows error state with retry when initial query fails
  • list-of-posts.tsx shows "Nothing more to load" only after successful data fetch
  • Client-side retry works after hydration for temporarily failed queries
  • This was discovered during production traffic testing (2026-01-05)
  • ~80% of requests returned 503 from api.hive.blog during high load
  • See also: #788 (closed) (renderer logging issues - merged)