Skip to content

Commit 72a5e4f

Browse files
authored
fix(replay): Set text/javascript MIME type on compression worker Blob (#22377)
tbh I can't find a URL that says safari is more prone to not load a worker blob if it doesn't have a MIME type, but seems reasonable to add one. ## Slop `getWorkerURL()` builds the Replay compression worker from a `Blob` URL, but the `Blob` was created without a MIME type, so it defaulted to an empty type. Safari (especially on iOS) validates the MIME type before executing a `Blob` as a classic `Worker` and silently rejects a non-JS type — firing a bare `error` event with **no message**, which the SDK surfaces as: > Failed to load Replay compression worker: Unknown error. This can happen due to CSP policy restrictions, network issues, or the worker script failing to load. Blink/Gecko are lenient about the blob MIME type, so this particular failure mode is Safari-specific. Tagging the blob `text/javascript` is the standard cross-browser fix. ```ts const workerBlob = new Blob([workerString], { type: 'text/javascript' }); ``` This is a handled error — recording already falls back to the uncompressed buffer — so there is no user-facing behavior change beyond the worker actually loading on Safari. ## Testing - New `packages/replay-worker/test/unit/getWorkerURL.test.ts` asserts the blob is created with the `text/javascript` MIME type. - `oxlint` + `oxfmt` clean; `replay-worker` suite passes. ## Notes This is the low-risk half of a split. A separate follow-up PR handles the cross-browser (Chrome/Edge/Firefox) occurrences of the same captured error, which come from the worker fetch being aborted during page navigation/teardown rather than from the MIME type. Follow-up to #19008, which introduced the descriptive error message but did not address the load failure itself.
1 parent d092fd4 commit 72a5e4f

3 files changed

Lines changed: 36 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@
7575

7676
Work in this release was contributed by @PeterWadie and @akshitsinha. Thank you for your contributions!
7777

78+
### Other Changes
79+
80+
- fix(replay): Set `text/javascript` MIME type on the compression worker Blob ([#22377](https://github.com/getsentry/sentry-javascript/pull/22377))
81+
7882
## 10.66.0
7983

8084
- chore(node-core): Deprecate `@sentry/node-core` package ([#22285](https://github.com/getsentry/sentry-javascript/pull/22285))

packages/replay-worker/src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import workerString from './worker';
44
* Get the URL for a web worker.
55
*/
66
export function getWorkerURL(): string {
7-
const workerBlob = new Blob([workerString]);
7+
// Safari (particularly on iOS) validates the MIME type of a Blob before it
8+
// will execute it as a classic Worker script. A Blob created without an
9+
// explicit `type` defaults to an empty string, which WebKit may reject,
10+
// firing a bare `error` event with no message. Blink/Gecko are lenient here,
11+
// so this only manifests on Safari. Set an explicit JavaScript MIME type so
12+
// the worker loads across browsers.
13+
const workerBlob = new Blob([workerString], { type: 'text/javascript' });
814
return URL.createObjectURL(workerBlob);
915
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @vitest-environment jsdom
3+
*/
4+
5+
import { describe, expect, it, vi } from 'vitest';
6+
import { getWorkerURL } from '../../src';
7+
8+
describe('getWorkerURL', () => {
9+
// Safari (esp. iOS) rejects executing a Blob worker without a JavaScript MIME
10+
// type, firing a bare `error` event. The Blob must be tagged `text/javascript`
11+
// so the worker loads across browsers.
12+
it('creates the worker Blob with a JavaScript MIME type', () => {
13+
// jsdom does not implement `URL.createObjectURL`, so stub it and capture the Blob.
14+
const createObjectURL = vi.fn<(blob: Blob) => string>().mockReturnValue('blob:mock');
15+
URL.createObjectURL = createObjectURL as unknown as typeof URL.createObjectURL;
16+
17+
const url = getWorkerURL();
18+
19+
expect(url).toBe('blob:mock');
20+
expect(createObjectURL).toHaveBeenCalledTimes(1);
21+
const blob = createObjectURL.mock.calls[0]![0];
22+
expect(blob).toBeInstanceOf(Blob);
23+
expect(blob.type).toBe('text/javascript');
24+
});
25+
});

0 commit comments

Comments
 (0)