diff --git a/app/src/providers/CoreStateProvider.tsx b/app/src/providers/CoreStateProvider.tsx index ad0578b2e..30c237904 100644 --- a/app/src/providers/CoreStateProvider.tsx +++ b/app/src/providers/CoreStateProvider.tsx @@ -75,6 +75,19 @@ export function coreStatePollFailureWarningMessage(failureCount: number): string return null; } +export function coreStatePollFailureDebugMessage(failureCount: number): string | null { + if (failureCount <= 0) { + return null; + } + if (failureCount < MAX_BOOTSTRAP_RETRIES) { + return `refresh failed during bootstrap retry ${failureCount}/${MAX_BOOTSTRAP_RETRIES}; nextAction=retrying`; + } + if (failureCount === MAX_BOOTSTRAP_RETRIES) { + return `refresh failed during bootstrap retry ${failureCount}/${MAX_BOOTSTRAP_RETRIES}; nextAction=marking-ready-with-fallback`; + } + return `refresh failed after ${failureCount} consecutive poll failures; bootstrapRetryLimit=${MAX_BOOTSTRAP_RETRIES}; nextAction=continuing-background-polling-with-warnings-suppressed`; +} + function decodeJwtPayload(token: string): Record | null { const [, payload] = token.split('.'); if (!payload) return null; @@ -425,12 +438,10 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) if (!cancelled) { bootstrapFailCountRef.current += 1; const safe = sanitizeError(error); - log( - 'refresh failed attempt=%d/%d error=%O', - bootstrapFailCountRef.current, - MAX_BOOTSTRAP_RETRIES, - safe - ); + const debugMessage = coreStatePollFailureDebugMessage(bootstrapFailCountRef.current); + if (debugMessage) { + log('%s error=%O', debugMessage, safe); + } const warningMessage = coreStatePollFailureWarningMessage(bootstrapFailCountRef.current); if (warningMessage) { console.warn(warningMessage, safe); diff --git a/app/src/providers/__tests__/CoreStateProvider.test.tsx b/app/src/providers/__tests__/CoreStateProvider.test.tsx index 26da31e29..499035596 100644 --- a/app/src/providers/__tests__/CoreStateProvider.test.tsx +++ b/app/src/providers/__tests__/CoreStateProvider.test.tsx @@ -7,6 +7,7 @@ import * as tauriCommands from '../../utils/tauriCommands'; import { getCoreStateSnapshot, setCoreStateSnapshot } from '../../lib/coreState/store'; import { setActiveUserId } from '../../store/userScopedStorage'; import CoreStateProvider, { + coreStatePollFailureDebugMessage, coreStatePollFailureWarningMessage, useCoreState, } from '../CoreStateProvider'; @@ -524,4 +525,20 @@ describe('coreStatePollFailureWarningMessage', () => { ); expect(coreStatePollFailureWarningMessage(7)).toBeNull(); }); + + it('describes post-bootstrap poll failures without impossible retry counters', () => { + expect(coreStatePollFailureDebugMessage(0)).toBeNull(); + expect(coreStatePollFailureDebugMessage(1)).toBe( + 'refresh failed during bootstrap retry 1/5; nextAction=retrying' + ); + expect(coreStatePollFailureDebugMessage(5)).toBe( + 'refresh failed during bootstrap retry 5/5; nextAction=marking-ready-with-fallback' + ); + + const postBootstrapMessage = coreStatePollFailureDebugMessage(11); + expect(postBootstrapMessage).toBe( + 'refresh failed after 11 consecutive poll failures; bootstrapRetryLimit=5; nextAction=continuing-background-polling-with-warnings-suppressed' + ); + expect(postBootstrapMessage).not.toContain('11/5'); + }); });