Merge pull request #1033 from heygen-com/fix/studio-rejection-flood

fix(studio): stop 3M/day rejection flood from composition 404s
This commit is contained in:
Miguel Ángel
2026-05-22 19:30:39 +02:00
committed by GitHub
2 changed files with 40 additions and 1 deletions
@@ -18,6 +18,7 @@ export type ScheduledSource = {
export class WebAudioTransport {
private _ctx: AudioContext | null = null;
private _bufferCache = new Map<string, AudioBuffer>();
private _failedSrcs = new Set<string>();
private _activeSources: ScheduledSource[] = [];
private _masterGain: GainNode | null = null;
// Composition-time reference frame: at AudioContext time `_rateAnchorCtx`,
@@ -53,14 +54,21 @@ export class WebAudioTransport {
const src = el.currentSrc || el.getAttribute("src");
if (!src) return null;
if (this._bufferCache.has(src)) return this._bufferCache.get(src)!;
if (this._failedSrcs.has(src)) return null;
if (!this._ctx) return null;
try {
const response = await fetch(src);
if (!response.ok) {
this._failedSrcs.add(src);
swallow("webAudioTransport.fetch", new Error(`${response.status} ${src}`));
return null;
}
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await this._ctx.decodeAudioData(arrayBuffer);
this._bufferCache.set(src, audioBuffer);
return audioBuffer;
} catch (err) {
this._failedSrcs.add(src);
swallow("webAudioTransport.decode", err);
return null;
}
+32 -1
View File
@@ -22,6 +22,16 @@ function errorProps(value: unknown): {
return { error_message: String(value), error_name: null, stack_trace: null };
}
function isCompositionAssetError(msg: string): boolean {
return msg.includes("Error fetching") && (msg.includes("404") || msg.includes("Not Found"));
}
const ERROR_CAP = 50;
let errorCount = 0;
let rejectionCount = 0;
let errorCapSent = false;
let rejectionCapSent = false;
window.addEventListener("error", (event) => {
if (event.message?.includes("ResizeObserver loop")) {
event.stopImmediatePropagation();
@@ -29,6 +39,15 @@ window.addEventListener("error", (event) => {
return;
}
errorCount++;
if (errorCount > ERROR_CAP) {
if (!errorCapSent) {
errorCapSent = true;
trackStudioEvent("error_cap_reached", { count: errorCount });
}
return;
}
trackStudioEvent("unhandled_error", {
...errorProps(event.error),
error_message: event.message,
@@ -39,7 +58,19 @@ window.addEventListener("error", (event) => {
});
window.addEventListener("unhandledrejection", (event) => {
trackStudioEvent("unhandled_promise_rejection", errorProps(event.reason));
const props = errorProps(event.reason);
if (isCompositionAssetError(props.error_message)) return;
rejectionCount++;
if (rejectionCount > ERROR_CAP) {
if (!rejectionCapSent) {
rejectionCapSent = true;
trackStudioEvent("rejection_cap_reached", { count: rejectionCount });
}
return;
}
trackStudioEvent("unhandled_promise_rejection", props);
});
createRoot(document.getElementById("root")!).render(