Skip to content

Reject asset load failures with an Error naming the URL - #149

Merged
changt merged 3 commits into
mainfrom
name-failed-assets
Oct 23, 2026
Merged

changt merged 3 commits into
mainfrom
name-failed-assets

Conversation

@changt

@changt changt commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #142.

What

A failed background image or tasks-vision fileset reached callers as a bare DOM Event. e.message was undefined, String(e) was "[object Event]", and the library's own log printed Event. Two places produced it:

  • Our background image loader. loadAndSetBackground did img.onerror = (err) => reject(err), which rejected with the <img> error event. It now rejects with Error("Failed to load background image from <path>").
  • MediaPipe's wasm loader. ImageSegmenter.createFromOptions loads its loader through a <script> tag and rejects with that tag's error event. That code is in @mediapipe/tasks-vision, so init() catches the rejection. If it's an Event, init() rethrows Error("Failed to load the MediaPipe tasks-vision fileset from <script src>"). Any other failure passes through unchanged. MediaPipe's model fetch already throws an Error that includes the path, so it isn't wrapped.

Both errors keep the original event as cause. It's assigned on the error after construction, not passed as the constructor's cause option, because browsers that don't support that option drop it silently.

Test

Both fail with the source change reverted. The full e2e suite passes (20/20).

Includes a patch changeset.

A failed background image or tasks-vision fileset reached callers as a bare
DOM Event, with no message, so `e.message` was undefined and logs printed
"Event". The image loader rejected with the <img> error event directly; it
now rejects with an Error naming the path. MediaPipe rejects a failed
wasm-loader <script> with that tag's error event, so init() converts it into
an Error naming the script URL. Both keep the original event as `cause`.

Adds e2e case B4, which points the fileset at a 404 and asserts the
rejection is an Error naming it, and extends D6 to assert the logged image
failure names the image. Both fail without this change.

Fixes #142
@changeset-bot

changeset-bot Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d3f489b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@livekit/track-processors Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment on lines +86 to +88
throw new Error(`Failed to load the MediaPipe tasks-vision fileset from ${src}`, {
cause: err,
});

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.

suggestion: I forget if there's an explicit error handling pattern this repo has been using (if so, it probably makes sense to follow that), but I wonder if it makes sense to add in some custom error types to make these types of issues easier to catch?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There isn't one. So I think it's better to just use plain errors since we have only 2 instances of this.

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.

Ok, yea, I mean I think this is ok, but I'm wondering if it makes sense to introduce something? We've got quite a few bare errors now and it might make sense to make them easier to catch. But this could be a follow up.

img.onload = () => resolve(img);
img.onerror = (err) => reject(err);
img.onerror = (event) =>
reject(new Error(`Failed to load background image from ${path}`, { cause: event }));

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.

suggestion: Same thing here. Also, if there's a custom error type, then IMO it might be good to add that path value as a property?

Comment thread tsconfig.json Outdated
{
"compilerOptions": {
"lib": ["DOM", "ES2018"],
"lib": ["DOM", "ES2018", "ES2022.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.

nitpick: FYI that while this adds typing support for Error.prototype.cause, something like new Error('foo', { cause: new Error('bar') }) won't actually work on older browsers because cause is a newer feature.

To work around this in livekit-client, this was done instead: https://github.com/livekit/client-sdk-js/blob/db8d49c69950539c08167f0e505b0df2bd76f41a/src/room/errors.ts#L4-L19. This way cause is assiged directly on the error and not passed into the constructor via super. If you migrate to custom errors instead of Error, I'd recommend you do something similar to handle that cause property.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Browsers without support for the Error constructor's cause option drop it
silently, so the original Event was lost there. cause is now assigned on the
error after construction, and the ES2022.Error lib entry is no longer needed.
B4 now checks that the fileset rejection carries the script tag's error
Event as cause. It fails if errorWithCause stops setting cause.
@changt
changt merged commit 50df5d2 into main Oct 23, 2026
3 checks passed
@changt
changt deleted the name-failed-assets branch October 23, 2026 16:47
@github-actions github-actions Bot mentioned this pull request Oct 23, 2026
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.

Asset load failures surface as bare DOM Events, so e.message is undefined and errors are unreportable

2 participants