Skip to content

fix(frontend): suppress TanStack Query default retries for authentication errors #392

@jeffmccune

Description

@jeffmccune

Parent Issue

Part of #390

Goal

Configure TanStack Query's global QueryClient to not retry requests that fail with authentication errors (401/Unauthenticated). By default, TanStack Query retries failed requests 3 times with exponential backoff, which amplifies the 401 burst (3x the number of failing requests). After the interceptor from #391 handles the first 401, TanStack Query should not add its own retry attempts for auth errors.

Acceptance Criteria

  • TanStack Query does NOT retry queries that fail with ConnectRPC Unauthenticated errors
  • Non-auth errors still retry normally (default TanStack Query behavior preserved)
  • Unit test verifies the retry function returns false for Unauthenticated errors
  • Unit test verifies the retry function returns true for other errors
  • make test passes

Implementation Notes

Files to modify

  • frontend/src/main.tsx (or wherever QueryClient is created) — Add a custom retry function to defaultOptions.queries

Approach

import { ConnectError, Code } from '@connectrpc/connect'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: (failureCount, error) => {
        // Don't retry authentication errors — the interceptor handles renewal
        if (error instanceof ConnectError && error.code === Code.Unauthenticated) {
          return false
        }
        // Default retry behavior for other errors (3 retries)
        return failureCount < 3
      },
    },
  },
})

Testing approach

Create or update test for the retry function:

  • Test with a ConnectError with Code.Unauthenticated → returns false
  • Test with a ConnectError with other codes (e.g., Code.Internal) → returns true for failureCount < 3
  • Test with a regular Error → returns true for failureCount < 3

Dependencies

Depends on #391 — the interceptor should be in place first so that 401s are handled at the transport level before TanStack Query sees them. However, this phase can technically be implemented in parallel since it's a separate concern (retry suppression vs. renewal).

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