Skip to content

Upload Media: Add error taxonomy, localized messages, and dev diagnostics#74917

Merged
adamsilverstein merged 51 commits into
trunkfrom
74366-add-retry-and-error-handling
Jun 18, 2026
Merged

Upload Media: Add error taxonomy, localized messages, and dev diagnostics#74917
adamsilverstein merged 51 commits into
trunkfrom
74366-add-retry-and-error-handling

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Jan 23, 2026

Copy link
Copy Markdown
Member

Fixes #74366

Summary

Adds an error-code taxonomy, localized error messages, and dev-only diagnostics to @wordpress/upload-media.

This started as half of a combined error-handling + retry PR. Auto-retry has since landed separately in #76765, which classifies retryable failures by matching fetch/api-fetch error messages (uploads reject with plain Error, not UploadError). The retry-classification scaffolding originally in this PR (UploadError#isRetryable, a RETRYABLE_CODES allowlist, and aspirational NETWORK_ERROR / TIMEOUT_ERROR / SERVER_ERROR codes) was therefore redundant and has been removed - this PR no longer overlaps with retry.

What's in this PR

Error taxonomy

  • ErrorCode enum covering every code the package actually throws (EMPTY_FILE, SIZE_ABOVE_LIMIT, MIME_TYPE_NOT_SUPPORTED, MIME_TYPE_NOT_ALLOWED_FOR_USER, HEIC_DECODE_ERROR, IMAGE_TRANSCODING_ERROR, IMAGE_ROTATION_ERROR, MEDIA_TRANSCODING_ERROR, GENERAL).
  • All existing throw sites (validate-file-size, validate-mime-type, validate-mime-type-for-user, store/private-actions) migrated from bare string codes to ErrorCode members.
  • error-messages.ts - localized, i18n-ready message map (title / description / optional action) for every code, with a sane fallback.
  • ErrorCode, getErrorMessage, and the ErrorMessageConfig type are exported from the package entry point so consumers can render friendly messages off a raw UploadError. Not yet wired into any Notice UI - rendering these in the editor is a follow-up.

Dev-only diagnostic logging

  • store/utils/debug-logger.ts exposes a debug() helper that writes to console.debug. Unlike @wordpress/warning it doesn't throw a caught Error, so it won't trip "pause on caught exceptions".
  • cancelItem logs item cancellations and batch completions via this console.debug diagnostic; top-level cancellations without an onError handler are surfaced via console.error. All gated on SCRIPT_DEBUG, so no production overhead.

Performance instrumentation (User Timings API)

  • store/utils/debug-logger.ts exposes a measure() helper that writes entries to a custom "Upload Media" track in the DevTools Performance panel.
  • Instrumented: uploadItem, sideloadItem, resizeCropItem, rotateItem, transcodeImageItem.
  • Gated on globalThis.SCRIPT_DEBUG, so production builds skip the performance.measure call entirely.

Incidental fix

  • uploadItem now guards against double-dispatch of finishOperation when both onFileChange and onSuccess fire for the same attachment (idempotent finishUpload closure).

Test plan

  • Unit tests pass (281 tests across @wordpress/upload-media), including new coverage for getErrorMessage (every ErrorCode maps to a message, file name interpolation, unknown-code fallback)
  • prettier --check / lint clean on changed files
  • Manual: upload with SCRIPT_DEBUG = true, verify the console.debug diagnostics surface on cancel and batch completion
  • Manual: with SCRIPT_DEBUG = true, confirm the "Upload Media" track appears in the DevTools Performance panel for an upload with sub-size sideloads
  • Manual: upload a file over maxUploadFileSize, verify the resulting UploadError passed to onError has code === ErrorCode.SIZE_ABOVE_LIMIT

@github-actions

This comment was marked as outdated.

@github-actions

github-actions Bot commented Jan 23, 2026

Copy link
Copy Markdown

Size Change: +812 B (+0.01%)

Total Size: 8.6 MB

📦 View Changed
Filename Size Change
build/scripts/upload-media/index.min.js 13.5 kB +812 B (+6.38%) 🔍

compressed-size-action

@github-actions

github-actions Bot commented Jan 24, 2026

Copy link
Copy Markdown

Flaky tests detected in 0da5356.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/27751942722
📝 Reported issues:

@adamsilverstein adamsilverstein marked this pull request as draft January 24, 2026 02:13
@adamsilverstein adamsilverstein force-pushed the 74366-add-retry-and-error-handling branch from 22b6f13 to 34b509a Compare January 24, 2026 02:17
@github-actions github-actions Bot added [Package] Block library /packages/block-library [Package] Block editor /packages/block-editor labels Jan 24, 2026
@adamsilverstein adamsilverstein force-pushed the 74366-add-retry-and-error-handling branch from c5d67ea to 7d17226 Compare January 24, 2026 02:26
@github-actions github-actions Bot removed [Package] Core data /packages/core-data [Package] Block library /packages/block-library [Package] Block editor /packages/block-editor [Package] Sync labels Jan 24, 2026
@adamsilverstein adamsilverstein requested review from Copilot and removed request for nerrad January 24, 2026 05:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements comprehensive error handling and retry logic for the upload-media package to address GitHub Issue #74366. The implementation adds an error classification system with 16 error types, automatic retry with exponential backoff for transient failures, user-friendly localized error messages, and retry state management with a new ItemStatus.PendingRetry status.

Changes:

  • Added ErrorCode enum with 16 error types categorized as retryable (network, timeout, server, VIPS worker errors) and non-retryable (validation, permission, file size, MIME type errors)
  • Implemented automatic retry with exponential backoff and jitter for failed uploads, with configurable retry settings (default: 3 attempts, 1s-30s delays, 2x multiplier)
  • Created comprehensive error message system with localized, actionable user guidance for all error types

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/upload-media/src/upload-error.ts Added ErrorCode enum and isRetryable getter to classify errors
packages/upload-media/src/error-messages.ts New file providing user-friendly i18n error messages for all error codes
packages/upload-media/src/store/utils/retry.ts New file with exponential backoff calculation and error classification logic
packages/upload-media/src/store/utils/debug-logger.ts Added logging functions for retry scheduling, execution, and max retries exceeded
packages/upload-media/src/store/types.ts Added RetrySettings, ScheduleRetryAction, ItemStatus.PendingRetry, and retry-related QueueItem fields
packages/upload-media/src/store/constants.ts Added retry configuration constants (max attempts, delays, multiplier, jitter)
packages/upload-media/src/store/reducer.ts Added ScheduleRetry case and default retry settings initialization
packages/upload-media/src/store/actions.ts Modified cancelItem to check retry eligibility, added scheduleRetry and executeRetry actions
packages/upload-media/src/store/private-selectors.ts Added selectors for retry state (pending items, retry timestamp, retry count, max retries check)
packages/upload-media/src/store/test/retry.ts New test file with 28 unit tests for retry utilities
packages/upload-media/src/index.ts Exported ErrorCode, error message functions, and retry-related types
packages/upload-media/README.md Updated documentation for cancelItem, scheduleRetry, and executeRetry actions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/upload-media/src/store/actions.ts Outdated
Comment thread packages/upload-media/src/store/types.ts Outdated
Comment thread packages/upload-media/src/store/reducer.ts
Comment thread packages/upload-media/src/store/actions.ts
Comment thread packages/upload-media/src/index.ts Outdated
Comment thread packages/upload-media/src/store/actions.ts Outdated
@adamsilverstein adamsilverstein linked an issue Jan 29, 2026 that may be closed by this pull request
5 tasks
@adamsilverstein adamsilverstein force-pushed the feature/client-side-media-dev branch from 1fb78d7 to 89e2a02 Compare February 2, 2026 20:04
@adamsilverstein adamsilverstein force-pushed the 74366-add-retry-and-error-handling branch 2 times, most recently from 9e4d2bc to 05918ca Compare February 3, 2026 19:06
@adamsilverstein adamsilverstein added [Type] Enhancement A suggestion for improvement. [Feature] Client Side Media Media processing in the browser with WASM [Status] In Progress Tracking issues with work in progress labels Feb 24, 2026
@adamsilverstein adamsilverstein self-assigned this Feb 24, 2026
@adamsilverstein adamsilverstein changed the base branch from feature/client-side-media-dev to trunk February 24, 2026 09:15
adamsilverstein and others added 2 commits February 24, 2026 16:31
Implements automatic retry with exponential backoff for failed uploads,
error classification system, and user-friendly error messages.

Changes:
- Add ErrorCode enum with 16 error types (network, validation, processing)
- Add isRetryable getter to UploadError class for automatic retry classification
- Add retry configuration constants (max attempts, delays, backoff multiplier)
- Add RetrySettings interface and ItemStatus.PendingRetry status
- Add scheduleRetry/executeRetry actions for automatic retry scheduling
- Add retry state selectors (getPendingRetryItems, getItemRetryCount, etc.)
- Add retry logging functions for debugging
- Add calculateRetryDelay utility with exponential backoff and jitter
- Add shouldRetryError utility for error classification
- Add user-friendly i18n error messages for all error codes
- Add comprehensive unit tests for retry utilities (28 tests)

The retry system automatically retries failed uploads for transient errors
(network failures, timeouts, server errors) while immediately failing for
non-retryable errors (validation, permissions, file size limits).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Resolve conflict in sideloadItem: keep measure() instrumentation while
adopting trunk's new onSuccess(subSize) callback that accumulates
SubSizeData onto the parent item.

@adamsilverstein adamsilverstein left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review from Claude

I took a pass through the PR after resolving the latest trunk merge conflict. A few substantive issues to flag before this lands, plus a couple of cleanups.

🔴 Error-handling infrastructure is not wired into the code it's meant to handle

The new ErrorCode enum + error-messages.ts + isRetryable getter form a nice vocabulary, but nothing in the package actually produces those codes, and nothing consumes the helpers:

  • Actual new UploadError( { code: … } ) sites use string codes like 'HEIC_DECODE_ERROR', 'EMPTY_FILE', 'SIZE_ABOVE_LIMIT', 'MIME_TYPE_NOT_ALLOWED_FOR_USER', 'MIME_TYPE_NOT_SUPPORTED', 'MEDIA_TRANSCODING_ERROR', 'IMAGE_TRANSCODING_ERROR', 'IMAGE_ROTATION_ERROR' — none of which appear in ErrorCode.
  • RETRYABLE_CODES contains NETWORK_ERROR, TIMEOUT_ERROR, SERVER_ERROR, VIPS_WORKER_ERROR. Since none of those codes is ever thrown in the current code paths, upload-error.isRetryable effectively always returns false.
  • getErrorMessage() / getRetryMessage() / getMaxRetriesExceededMessage() are not imported from anywhere, and ErrorCode / these helpers are not re-exported from packages/upload-media/src/index.ts, so no consumer (this package or plugin code) can use them either.

Suggested direction before merge (pick one):

  1. Minimum: migrate all existing throw sites to use ErrorCode values, add ErrorCode to the enum for the remaining legacy codes (HEIC_DECODE_ERROR, EMPTY_FILE, SIZE_ABOVE_LIMIT, MIME_TYPE_NOT_ALLOWED_FOR_USER, MIME_TYPE_NOT_SUPPORTED, MEDIA_TRANSCODING_ERROR), and export ErrorCode + getErrorMessage from index.ts. Also type UploadError.code as ErrorCode | string instead of raw string.
  2. Or: drop error-messages.ts and ErrorCode from this PR and land them alongside the UI that consumes them. Otherwise CI will happily ship dead code.

🟡 debug-logger.ts is a compile-time no-op

DEBUG_ENABLED = false is a module-level constant, so measure() always short-circuits in every build. If the goal is to leave hooks in the code for people to turn on during local work, that's fine — but it's worth being explicit:

  • Gate it with process.env.NODE_ENV === 'development' or globalThis.SCRIPT_DEBUG, matching the @wordpress/warning pattern. Or
  • Expose a setDebugEnabled() / read from a well-known global so it can be flipped without editing the source.

Otherwise a future reader has to grep the file to find the constant and rebuild the package to use it.

🟡 PR description is stale

  • README.md is listed as modified, but the diff shows no README change.
  • "Retry logic will be handled in a separate PR" — true for auto-retry/scheduling, but the user-initiated retryItem action, retryCount field on QueueItem, and RetryItem reducer/tests are all here (they actually landed in earlier trunk work, but the current description doesn't acknowledge that context).
  • getRetryMessage() and getMaxRetriesExceededMessage() shipped with this PR but are scoped to the retry UX that's explicitly not in this PR.

Description should reflect: "adds error-handling vocabulary + logging instrumentation, no consumer wiring yet (follow-up PR)" — or you wire up the consumer and update the description accordingly.

🟡 test-results/.last-run.json is accidentally committed

Already flagged by @andrewserong. Still in the branch:

 test-results/.last-run.json                        |  10 +

Please remove and add test-results/ to .gitignore (currently only test/storybook-playwright/test-results is ignored).

🟡 Unrelated test additions

packages/upload-media/src/store/test/reducer.ts adds tests for PauseQueue, ResumeQueue, CacheBlobUrl, RevokeBlobUrls — which are good tests, but none are touched by this PR. If the intent is "improve reducer coverage while I'm here," that's reasonable to keep, but mention it in the description. Otherwise consider splitting them out so this PR stays focused.

🟡 No CHANGELOG entry

packages/upload-media/CHANGELOG.md has an Unreleased entry from #75257 but nothing for this PR. Add a bullet.

🟢 Nice touches

  • The finishUpload idempotency guard in uploadItem (preventing double-dispatch when both onFileChange and onSuccess fire) is a good incidental fix. Worth calling out in the description so it doesn't look like noise.
  • @wordpress/warning calls in cancelItem are useful in dev builds; they only emit under SCRIPT_DEBUG, so no prod overhead. 👍
  • Sideload measure() instrumentation plays nicely with the new onSuccess(subSize) trunk API after the merge.

Nits

  • error-messages.ts: getErrorMessage takes code: string; once the enum is wired up, tightening that to ErrorCode | string will catch typos at callsites.
  • The batch-completed warning() fires inside the cancelItem finishing branch, which means a silent cancel of the last item in a batch still logs "Batch completed". If that's intentional, fine, but worth a comment.

@adamsilverstein

adamsilverstein commented Apr 21, 2026

Copy link
Copy Markdown
Member Author

Final sweep + suggested PR description

Merge conflict vs trunk is resolved and pushed (21a8c0c5980) — the sideload change now uses trunk's onSuccess(subSize) callback and keeps the new measure() timing. All 188 upload-media unit tests pass locally.

CodeRabbit (run via coderabbit review --plain --base origin/trunk) returned no findings against this PR's diff.

Remaining items worth addressing before merge

Summarizing from my earlier review for visibility:

  1. Wire up ErrorCode or drop it. The enum + isRetryable + error-messages.ts currently have no producers and no consumers. Either migrate existing throw sites to the enum and export from index.ts, or defer this file to the follow-up PR that actually consumes it. Shipping as-is means dead code in npm for one release cycle.
  2. Delete test-results/.last-run.json (already flagged by @andrewserong) and consider adding test-results/ to .gitignore. The file is still in the diff.
  3. debug-logger.ts gate. DEBUG_ENABLED = false is a compile-time constant — measure() is always a no-op unless someone edits the file. If the goal is "dev-only instrumentation," gate on process.env.NODE_ENV === 'development' or globalThis.SCRIPT_DEBUG (matching @wordpress/warning); otherwise document that this is an opt-in developer tool.
  4. Add a CHANGELOG.md entry under the existing ## Unreleased section of packages/upload-media/CHANGELOG.md.
  5. Retry helpersgetRetryMessage() / getMaxRetriesExceededMessage() are for the retry UX you've said is out of scope. If the follow-up PR is imminent, leaving them is fine; otherwise consider removing them to keep this PR focused on error classification + logging.

adamsilverstein and others added 12 commits April 21, 2026 11:10
Delete test-results/.last-run.json (Playwright run artifact) and add
test-results/ to .gitignore so it won't be committed again.
Replace DEBUG_ENABLED = false with a runtime check of globalThis.SCRIPT_DEBUG,
matching the @wordpress/warning pattern. Production builds become no-ops
automatically, developers can flip SCRIPT_DEBUG to see the Upload Media
track in the DevTools Performance panel without editing the source.
Drop getRetryMessage() and getMaxRetriesExceededMessage() — these
belong with the retry scheduling work shipping in a follow-up PR,
not the error-classification scaffolding.
Previously the ErrorCode enum and error-messages.ts helpers were
scaffolding with no producers or consumers: throw sites used bare
strings ('HEIC_DECODE_ERROR', 'EMPTY_FILE', etc.), isRetryable
always returned false, and nothing was exported from index.ts.

Changes:
- Rework ErrorCode to include the codes actually thrown today
  (EMPTY_FILE, SIZE_ABOVE_LIMIT, MIME_TYPE_NOT_SUPPORTED,
  MIME_TYPE_NOT_ALLOWED_FOR_USER, HEIC_DECODE_ERROR,
  IMAGE_TRANSCODING_ERROR, IMAGE_ROTATION_ERROR,
  MEDIA_TRANSCODING_ERROR) plus retry-targets kept for the
  follow-up PR (NETWORK_ERROR, TIMEOUT_ERROR, SERVER_ERROR).
- Drop speculative codes that nothing threw or consumed.
- Migrate validate-file-size, validate-mime-type,
  validate-mime-type-for-user, and private-actions throw sites
  to use ErrorCode members instead of string literals.
- Rewrite error-messages.ts so every enum entry has a matching
  localized message, and tighten getErrorMessage's parameter
  type to ErrorCode | string.
- Export ErrorCode, getErrorMessage, and ErrorMessageConfig
  from the package index so consumers can actually use them.
Resolve conflicts in packages/upload-media/package.json and
package-lock.json by keeping the new @wordpress/warning dependency
introduced in this PR alongside trunk's uuid bump to ^14.0.0.

Move the PR's changelog entries back under the Unreleased heading
since this PR is not yet merged; trunk's 0.30.0 release contains
only the #75257 entry.
Resolve conflicts in upload-media CHANGELOG and store actions:
- CHANGELOG: combine Unreleased entries from both sides; keep trunk's
  scaled-suffix bug fixes alongside this PR's enhancements.
- actions.ts: keep trunk's parentId guard on the cancellation logger and
  layer this PR's warning() call inside it; adopt trunk's local batchId
  variable in the batch-complete check.
- private-actions.ts: keep this PR's ErrorCode enum members on the
  IMAGE_TRANSCODING_ERROR / IMAGE_ROTATION_ERROR throws while adopting
  trunk's localized user-facing messages.
…error-handling

# Conflicts:
#	packages/upload-media/CHANGELOG.md
…error-handling

# Conflicts:
#	packages/upload-media/CHANGELOG.md
The cancelItem retry-integration tests exercise the cancel-logging path,
where cancelItem() deliberately calls warning() for silent cancels and
for top-level items without an onError handler. The jest-console guard
failed those tests because the warning was unexpected. Assert
toHaveWarned() so the expected diagnostic is acknowledged.
@andrewserong

andrewserong commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

What's the status of this PR? I remember there being quite a bit of overlap with the retry PR, and in its current form it looks like getErrorMessage isn't wired up to anything. Is the goal with this PR to mostly add dev mode logging and instrumentation, or is it about user facing error handling?

(Apologies, I couldn't remember where we were up to with it!)

The auto-retry follow-up (#76765) shipped its own retryability check based
on error-message patterns (RETRYABLE_MESSAGE_PATTERNS in store/utils/retry.ts),
because uploads reject with plain Error instances from fetch/api-fetch rather
than UploadError carrying a code. That leaves this PR's UploadError#isRetryable
getter, the RETRYABLE_CODES allowlist, and the aspirational NETWORK_ERROR /
TIMEOUT_ERROR / SERVER_ERROR codes (and their messages) unused and unproduced.

Remove them so the ErrorCode enum only describes failures the package actually
throws, keeping getErrorMessage as the user-facing message layer.

Also relocate the unmerged #74917 CHANGELOG entries from the released 0.33.0
section back to Unreleased, where they belong, and drop the isRetryable mention.
@adamsilverstein

Copy link
Copy Markdown
Member Author

Good memory, you were right on both counts. The retry follow-up (#76765) already merged and classifies retryability via error-message patterns, so this PR's isRetryable getter and the retryable NETWORK/TIMEOUT/SERVER codes were redundant - I've stripped them in 5d752b6.

So the scope now is diagnostics + error taxonomy, not user-facing error handling per se:

  • ErrorCode enum migration of existing throw sites + i18n getErrorMessage() helper (exported for consumers to render, but not yet wired into any Notice UI - that'd be a follow-up).
  • Dev-only @wordpress/warning logging and a "Upload Media" DevTools Performance track, both gated on SCRIPT_DEBUG.
  • One incidental bug fix (idempotent finishOperation).

Updated the title/description to match.

@andrewserong andrewserong left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the extra context @adamsilverstein! Good idea to get this in, and also to keep the wiring up of the error messages to a follow-up 👍

This looks close to me, just left a bunch of comments from running the PR through a Claude session (so take the comments with a grain of salt, they might be a little off).

code: ErrorCode | string,
fileName: string
): ErrorMessageConfig {
const messages: Record< string, ErrorMessageConfig > = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiniest of nits: this function creates an object and then performs a lookup to it, but it'll only ever look up one item. We could also do this via a switch statement which might read a little neater (this is highly subjective though — if you prefer the way you've done it, ignore this comment!)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept the object map here — as you noted it's subjective, and I find the keyed config a touch more readable than a long switch. Happy to flip it if you feel strongly.

}

interface UploadErrorArgs {
code: string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR introduces the enum but currently doesn't appear to limit the type to it? (I might have gotten that wrong, so apologies if I've missed it!)

Should we also be typing things elsewhere to enforce use of the enum, or is the enum mostly a convenience to guard against typos? (Which is also fine as an approach)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional — code is typed ErrorCode | string so that codes originating outside this package (e.g. server-side or future call sites) still pass through without a type error. getErrorMessage() looks the code up in the enum-keyed map and falls back to GENERAL for anything unrecognized, so the enum drives the known set while the | string keeps it forward-compatible.

Comment on lines 213 to 217
warning(
`Upload cancelled for item ${ id }: ${ error.message }`
);
// eslint-disable-next-line no-console -- Deliberately log errors here.
console.error( 'Upload cancelled', error );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now performs to logging calls (one a warning and one an error). Should we pick one?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consolidated with the same refactor — the diagnostic path now goes through the SCRIPT_DEBUG console.debug helper, leaving a single console.error for the no-handler case.

Comment thread packages/upload-media/src/store/actions.ts
Comment on lines +20 to +24
// User action
ABORTED = 'ABORTED',

// Generic fallback
GENERAL = 'GENERAL',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like both ABORTED and GENERAL aren't being used yet (though they do have string translations). Are they included here intentionally, or do they need wiring up?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GENERAL is now wired up as the fallback (thrown in actions.ts). ABORTED had no throw site, so I removed it in b72165c rather than ship an unused translated string.

} );
} );

describe( `${ Type.RevokeBlobUrls }`, () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these reducer tests seem unrelated to the other changes, are these leftovers from the other PR, or are these added intentionally?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, these were leftovers from when the retry logic lived on this branch. Removed in b8024dd; the reducer test file now matches trunk exactly. PauseQueue/ResumeQueue are already covered there (landed with the retry work in #76765), and CacheBlobUrl/RevokeBlobUrls were out of scope for this PR.

Replace the @wordpress/warning calls added for upload diagnostics with a
new SCRIPT_DEBUG-gated debug() helper in debug-logger.ts. warning()
deliberately throws and catches an Error so it can pause debuggers on
caught exceptions; pointing that at normal lifecycle events (silent
cancellations, batch completion) yanked any developer with that
breakpoint into the debugger on every batch finish. console.debug is the
right semantics for non-error diagnostics and does not throw.

The no-handler cancellation path now logs only console.error (a genuine
error) instead of both warning() and console.error. Drop the now-unused
@wordpress/warning dependency.

Also type UploadError.code as ErrorCode | string and use ErrorCode.GENERAL
in place of the bare 'UPLOAD_ERROR' string in the parent-cancel fallback,
wiring up the previously unused enum member.
@adamsilverstein

Copy link
Copy Markdown
Member Author

Thanks @andrewserong - good catches from that Claude session.

Addressed in ac1e4a6:

  • warning() semantics (actions.ts:333, :217): you were right that @wordpress/warning isn't the best tool for these diagnostics - it throws+catches an Error, so anyone with "pause on caught exceptions" enabled got pulled into the debugger on every batch completion. Added a SCRIPT_DEBUG-gated debug() helper in debug-logger.ts (plain console.debug, no throw) and routed the cancellation/batch diagnostics through it. The no-handler case now logs only console.error (a genuine error) instead of both. Dropped the now-unused @wordpress/warning dependency.
  • Enum typing (upload-error.ts:28): typed UploadError.code as ErrorCode | string.
  • Unused GENERAL (upload-error.ts:24): wired ErrorCode.GENERAL into the parent-cancel fallback, which previously used a bare 'UPLOAD_ERROR' string that wasn't even in the enum. ABORTED is intentionally kept for the follow-up Notice UI (cancelled uploads by deleting image mid-upload).

On the two I left as-is:

  • error-messages.ts object vs switch: keeping the lookup map - it reads cleanly and avoids a long switch, but happy to change if you feel strongly.
  • Reducer tests (reducer.ts): these are intentional coverage for adjacent reducer cases (PauseQueue/ResumeQueue/CacheBlobUrl/RevokeBlobUrls) that were previously untested; I'll note them in the description so they don't read as noise.

@andrewserong andrewserong left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, thanks for the updates! This feels like a good foundation for logging and dev diagnostics, and confirmed that the measure behaviour is showing up in dev tools as expected:

Image

LGTM 🚀

Comment thread packages/upload-media/tsconfig.json Outdated
{ "path": "../url" },
{ "path": "../vips" }
{ "path": "../vips" },
{ "path": "../warning" }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny nit: we no longer need this warning entry.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in 38ea9fd@wordpress/warning is no longer a dependency.

ErrorCode.ABORTED had a translated message but no throw site; the cancel
path logs via console.error rather than throwing an UploadError. Drop it
until a code path actually needs it (GENERAL remains the wired fallback).
Addresses review feedback from andrewserong.
PauseQueue and ResumeQueue are already covered by trunk's reducer tests
(landed with the retry work in #76765), and CacheBlobUrl/RevokeBlobUrls
test reducers unrelated to this PR's error-taxonomy scope. These were
leftovers from when retry logic lived on this branch; the reducer test
file now matches trunk. Addresses review feedback from andrewserong.
@wordpress/warning is no longer a dependency of the package after the
switch to a SCRIPT_DEBUG-gated console.debug helper. Addresses review
feedback from andrewserong.
The standard test commands write Playwright output to artifacts/test-results
(via WP_ARTIFACTS_PATH), which is already covered by the ignored /artifacts
directory. A root-level test-results/ only appears when running Playwright
directly, and this entry was added defensively after an accidental commit
during development. It is unrelated to this PR, so remove it.
Cover the new public API exported from the package entry point: every
ErrorCode maps to a non-empty title/description, the file name is
interpolated into the description, distinct codes yield distinct
messages, and an unknown code falls back to the generic GENERAL message.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Client Side Media Media processing in the browser with WASM Needs Testing Needs further testing to be confirmed. [Status] In Progress Tracking issues with work in progress [Type] Enhancement A suggestion for improvement.

Projects

Development

Successfully merging this pull request may close these issues.

Add comprehensive error handling

4 participants