{"record":{"id":"768d7f374db937c3","repo":"heygen-com/hyperframes","slug":"audio-fx-cancelled-for-track-options-trackid","errorCode":null,"errorMessage":"Audio FX cancelled for track ${options.trackId}","messagePattern":"Audio FX cancelled for track (.+?)","errorType":"exception","errorClass":"AudioFxRenderError","httpStatus":null,"severity":"info","filePath":"packages/engine/src/services/audioFxRender.ts","lineNumber":217,"sourceCode":"): Promise<string> {\n  if (enabledAudioFxNodes(chain).length === 0) return inputWav;\n  if (!existsSync(inputWav)) {\n    throw new AudioFxRenderError(`Audio FX input is missing: ${inputWav}`);\n  }\n\n  const { samples, sampleRate, channels } = readWav(inputWav);\n  const planes = deinterleave(samples, channels);\n\n  // Audio processing needs no GPU or special capture mode; a plain sandboxed\n  // browser is enough, and the lease pool reuses one across tracks.\n  const lease = await acquireBrowser([\n    \"--no-sandbox\",\n    \"--autoplay-policy=no-user-gesture-required\",\n  ]);\n  const hostDir = mkdtempSync(join(tmpdir(), \"hf-fx-host-\"));\n  try {\n    if (options.signal?.aborted) {\n      throw new AudioFxRenderError(`Audio FX cancelled for track ${options.trackId}`);\n    }\n    const page = await lease.browser.newPage();\n    try {\n      // AudioWorklet is only exposed in a secure context, and about:blank is\n      // not one — the module would fail with an opaque error. A file:// page\n      // qualifies and needs no listening socket.\n      const hostPage = join(hostDir, \"audio-fx.html\");\n      writeFileSync(hostPage, \"<!doctype html><meta charset=utf-8><title>audio fx</title>\");\n      await page.goto(pathToFileURL(hostPage).href, { waitUntil: \"domcontentloaded\" });\n      await page.addScriptTag({ content: getAudioFxRuntimeScript() });\n\n      const rendered = (await page.evaluate(\n        async ([channelB64, rate, chainJson]: [string[], number, string]) => {\n          const decode = (b64: string): Float32Array => {\n            const bin = atob(b64);\n            const bytes = new Uint8Array(bin.length);\n            for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n            return new Float32Array(bytes.buffer);","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/engine/src/services/audioFxRender.ts#L199-L235","documentation":"Thrown when options.signal?.aborted is true at the checkpoint inside applyAudioFxChain(), which runs after acquiring a browser lease but before opening a page. This is an intentional cancellation — the caller's AbortSignal fired, so the FX render is aborted cleanly rather than continuing to consume browser resources.","triggerScenarios":"applyAudioFxChain() is called with options.signal set to an AbortSignal that becomes aborted between the existsSync check and the post-acquireBrowser checkpoint. The throw happens inside the try block so the finally clauses release the browser lease and clean up the temp host directory.","commonSituations":"User cancels a render job mid-track. A pipeline-level timeout fires. A parent render orchestration aborts all child tasks on the first error. The AbortController is connected to a request deadline in a serverless function.","solutions":["This is expected behavior for cancellation — verify the AbortController was intentionally triggered and propagate the cancellation upstream.","If the abort is unexpected, trace which code called controller.abort() and under what condition.","Ensure the caller handles this as a cancellation (not a hard failure) so partial output is cleaned up."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (options.signal?.aborted) {\n  // skip the FX chain entirely, don't acquire a browser\n  return inputWav;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await applyAudioFxChain(inputWav, chain, outWav, opts);\n} catch (err) {\n  if (err instanceof AudioFxRenderError && err.message.includes('cancelled')) {\n    // graceful cancellation — clean up partial output, propagate AbortError\n    throw new DOMException('Aborted', 'AbortError');\n  }\n  throw err;\n}","preventionTips":["Check signal.aborted before calling applyAudioFxChain to avoid unnecessary browser leases.","Propagate cancellation as AbortError so upstream orchestrators can distinguish it from hard failures.","Ensure partial output WAVs are cleaned up on cancellation."],"tags":["audio","abort","cancellation","lifecycle"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}