Skip to content

[Feature] Add bounded retry with error classification to proof upload pipeline #3381

@numbers-official

Description

@numbers-official

Summary

The uploadProof$() method in DiaBackendAssetUploadingService uses an unbounded retryWhen with exponential backoff but no maximum retry limit and no error classification. Every error — including permanent, non-retryable errors like HTTP 403, 413, or server-side validation errors — triggers an infinite retry loop.

Impact

  1. Battery and network drain: On mobile devices, infinite retries waste battery and bandwidth indefinitely
  2. Upload queue deadlock: Because uploadTaskWorker$() uses concatMap, an infinitely-retrying proof blocks all subsequent proofs from uploading — a single failure deadlocks the entire pipeline
  3. No user feedback: No logging, telemetry, or notification when retries are occurring
  4. Deprecated API: retryWhen is deprecated in RxJS 7 and will be removed in a future version

Affected File

src/app/shared/dia-backend/asset/uploading/dia-backend-asset-uploading.service.ts — lines 152-158 (the retryWhen block within uploadProof$())

Related Bug: lodash.reject shadowing Promise reject

In src/app/shared/dia-backend/auth/dia-backend-auth.service.ts (line 440), the reject identifier used inside getToken() refers to lodash-es's reject() function (imported on line 5), NOT the Promise reject callback. When the token is empty, this calls lodash.reject(new Error(...)) which is a no-op, causing the Promise to hang forever. This silently deadlocks all 30+ authenticated API paths and compounds the upload retry issue — an upload hanging due to missing auth enters the infinite retry loop.

Suggested Implementation

Replace unbounded retryWhen with the modern retry() operator:

retry({
  count: 5, // Max retries
  delay: (error, retryCount) => {
    // Don't retry non-retryable HTTP errors
    if (error instanceof HttpErrorResponse) {
      const status = error.status;
      if ([401, 403, 413, 422].includes(status)) {
        return throwError(() => error);
      }
    }
    // Exponential backoff with jitter, capped at 60s
    const delay = Math.min(1000 * Math.pow(2, retryCount - 1) + Math.random() * 500, 60000);
    console.warn(`Upload retry ${retryCount}/5, delay: ${delay}ms`, error);
    return timer(delay);
  },
})

Also fix the getToken() bug by:

  1. Removing reject from the lodash-es import on line 5
  2. Using the Promise reject parameter properly in the new Promise<string>((resolve, reject) => {...}) constructor

Generated by Health Monitor with Omni

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions