diff --git a/admin/app/jobs/run_download_job.ts b/admin/app/jobs/run_download_job.ts index a0ee4f9..12b3532 100644 --- a/admin/app/jobs/run_download_job.ts +++ b/admin/app/jobs/run_download_job.ts @@ -67,7 +67,7 @@ export class RunDownloadJob { if (val) { await cancelRedis.del(RunDownloadJob.cancelKey(job.id!)) userCancelled = true - abortController.abort() + abortController.abort('user-cancel') } } catch { // Redis errors are non-fatal; in-process AbortController covers same-process cancels @@ -184,7 +184,8 @@ export class RunDownloadJob { } catch (error: any) { // Only prevent retries for user-initiated cancellations. BullMQ lock mismatches // can also abort the stream, and those should be retried with backoff. - if (userCancelled) { + // Check both the flag (Redis poll) and abort reason (in-process cancel). + if (userCancelled || abortController.signal.reason === 'user-cancel') { throw new UnrecoverableError(`Download cancelled: ${error.message}`) } throw error diff --git a/admin/app/services/download_service.ts b/admin/app/services/download_service.ts index 40cdca3..ac9d02d 100644 --- a/admin/app/services/download_service.ts +++ b/admin/app/services/download_service.ts @@ -125,7 +125,7 @@ export class DownloadService { await RunDownloadJob.signalCancel(jobId) // Also try in-memory abort (works if worker is in same process) - RunDownloadJob.abortControllers.get(jobId)?.abort() + RunDownloadJob.abortControllers.get(jobId)?.abort('user-cancel') RunDownloadJob.abortControllers.delete(jobId) // Poll for terminal state (up to 4s at 250ms intervals) — cooperates with BullMQ's lifecycle