Skip to content

fix: avoid misleading failure when metadata delete times out (DHIS2-15823) - #986

Draft
karolinelien wants to merge 5 commits into
dhis2:masterfrom
karolinelien:fix/misleading-delete-timeout-error
Draft

fix: avoid misleading failure when metadata delete times out (DHIS2-15823)#986
karolinelien wants to merge 5 commits into
dhis2:masterfrom
karolinelien:fix/misleading-delete-timeout-error

Conversation

@karolinelien

@karolinelien karolinelien commented Jul 1, 2026

Copy link
Copy Markdown

Summary

Fixes DHIS2-15823.

A long-running metadata delete — e.g. a Category Combo whose category option combos take a while to cascade-delete — can exceed a proxy/connection timeout. The client request is aborted or receives a gateway timeout while the backend keeps processing and completes the delete, but the UI showed a hard "Failed to delete" error, misleading the user into thinking nothing happened (and prompting duplicate delete attempts).

The original ticket described this in the legacy maintenance app ("undefined was not deleted"). That app is gone, but the same behaviour is still present in this app in a different form — this PR addresses it here.

Changes

  • Add isMaybeStillProcessingError() to detect network/aborted fetch errors and 408/502/503/504 gateway responses, where the operation may still be completing server-side.
  • On delete, when the error looks like a timeout, show a softer "The deletion may still be in progress" warning that prompts the user to refresh before retrying, instead of a hard failure.
  • Fix the {{messages}} placeholder in the failure message, which was never interpolated and always rendered empty; it now shows the backend message.

Notes / limitations

  • This is a frontend UX fix. The root cause — a synchronous, long-running delete holding the HTTP connection open — lives in dhis2-core. A proper long-term fix would make large cat-combo deletion asynchronous. The timeout detection here is necessarily a heuristic (the client cannot be 100% certain the backend succeeded), hence the "may still be in progress" wording.

Testing

  • yarn check-types
  • yarn d2-style check (eslint + prettier + tsc) ✅
  • New unit tests: src/lib/errors/errors.spec.ts (13 cases) ✅
  • categoryCombos/List.spec.tsx smoke test ✅

AI Assisted
@

…5823)

A long-running delete (eg. a Category Combo whose category option combos
take a while to cascade-delete) can exceed a proxy/connection timeout. The
client request is aborted or gets a gateway timeout while the backend keeps
processing and completes the delete, but the UI showed a hard "Failed to
delete" error - misleading the user into thinking nothing happened.

- Add isMaybeStillProcessingError() to detect network/aborted errors and
  408/502/503/504 gateway responses where the operation may still complete
  server-side.
- Show a softer "The deletion may still be in progress" warning in that case,
  prompting the user to refresh before retrying.
- Fix the {{messages}} placeholder in the failure message, which was never
  interpolated and always rendered empty; now shows the backend message.

AI Assisted

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 1, 2026

Copy link
Copy Markdown

Deploy Preview for dhis2-maintenance-app-beta ready!

Name Link
🔨 Latest commit fbd56fd
🔍 Latest deploy log https://app.netlify.com/projects/dhis2-maintenance-app-beta/deploys/6a4ba83e1dd4c100081d4a3e
😎 Deploy Preview https://deploy-preview-986.maintenance-app-beta.netlify.dhis2.org
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

- Detect gateway timeouts (408/502/503/504) even when the proxy returns a
  non-JSON body: app-runtime only populates details.httpStatusCode for DHIS2
  JSON responses, so fall back to parsing the status from the error message
  ("...statusText (504)"). Previously the 502/503/504 branch never fired
  against a real reverse-proxy timeout - the headline case for this fix.
- Drop redundant type cast and optional chaining on the non-optional
  FetchError.details field (extract getHttpStatusCode helper).
- Mark the status-code lookup table readonly; drop redundant cast in the test.
- Consolidate the two near-identical NoticeBox blocks into one to remove
  duplicated JSX.
- Add tests for proxy timeouts (status only in message) and for an unknown
  error with no status code.

AI Assisted

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@karolinelien

karolinelien commented Jul 1, 2026

Copy link
Copy Markdown
Author

Ran two independent reviews (correctness + SonarQube-style quality) and pushed fixes.

Material correctness fix (found in review): the original 502/503/504 detection relied on error.details.httpStatusCode, but app-runtime only populates that for DHIS2 JSON error bodies. A real reverse-proxy gateway timeout returns a non-JSON (HTML) body, so details is empty and the status survives only in the error message ("...statusText (504)"). The gateway branch therefore never fired against an actual proxy timeout — the headline case. Now getHttpStatusCode() falls back to parsing the status from the message. (network/aborted errors were already handled and are unaffected.)

Quality fixes:

  • Removed redundant cast + optional chaining on the non-optional FetchError.details field.
  • Marked the status-code lookup table readonly; dropped a redundant cast in the test.
  • Consolidated the two near-identical NoticeBox blocks into one (duplicated-JSX / CPD).
  • Added tests for proxy timeouts (status only in message) and for an unknown error with no status code. 18 unit tests pass; check-types + d2-style clean.

AI Assisted
@

karolinelien and others added 3 commits July 1, 2026 22:59
Convert MAYBE_STILL_PROCESSING_STATUS_CODES to a ReadonlySet and use .has()
instead of Array.includes(), resolving the SonarCloud code smell that failed
the quality gate.

AI Assisted

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Reword the timeout warning to "The request timed out. The operation may
  still be processing in the background, refresh to confirm."
- Add DeleteAction.spec.tsx covering the timeout ("still processing") branch
  and the hard-failure branch (backend error messages shown), giving the new
  delete-error handling test coverage.

AI Assisted

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Change the title to 'The operation may still be in progress'.

AI Assisted

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@karolinelien
karolinelien force-pushed the fix/misleading-delete-timeout-error branch from edcb1ec to fbd56fd Compare July 6, 2026 13:06
@sonarqubecloud

sonarqubecloud Bot commented Jul 6, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant