diff --git a/packages/mobile/src/components/download-track-archive-drawer/DownloadTrackArchiveDrawer.tsx b/packages/mobile/src/components/download-track-archive-drawer/DownloadTrackArchiveDrawer.tsx index 70c8eba9332..269caeded20 100644 --- a/packages/mobile/src/components/download-track-archive-drawer/DownloadTrackArchiveDrawer.tsx +++ b/packages/mobile/src/components/download-track-archive-drawer/DownloadTrackArchiveDrawer.tsx @@ -124,13 +124,27 @@ const DownloadTrackArchiveDrawerContent = ({ const { mutate: cancelStemsArchiveJob } = useCancelStemsArchiveJob() - const { data: jobState } = useGetStemsArchiveJobStatus({ + const { + data: jobState, + isError: isJobStatusError, + isTimedOut: isJobTimedOut + } = useGetStemsArchiveJobStatus({ jobId }) + // `isTimedOut` and `isError` have to be part of this, not just `failed`. + // A job that never leaves `waiting` — the archiver worker losing its Redis + // lock and holding every concurrency slot, for instance — is reported as a + // perfectly valid non-terminal state forever, so keying only off `failed` + // leaves the drawer spinning with no error and no retry until the user + // gives up. The shared hook already enforces STEMS_ARCHIVE_POLL_TIMEOUT_MS + // and hands back `isTimedOut`; web consumes it and mobile did not. const hasError = !isStartingDownload && - (downloadError || initiateDownloadFailed || jobState?.state === 'failed') + (downloadError || + initiateDownloadFailed || + jobState?.state === 'failed' || + (!!jobId && (isJobStatusError || isJobTimedOut))) useEffect(() => { if (hasError) {