{"record":{"id":"145f9e8753969b8a","repo":"heygen-com/hyperframes","slug":"not-a-wav-file-path","errorCode":null,"errorMessage":"Not a WAV file: ${path}","messagePattern":"Not a WAV file: (.+?)","errorType":"exception","errorClass":"AudioFxRenderError","httpStatus":null,"severity":"error","filePath":"packages/engine/src/services/audioFxRender.ts","lineNumber":72,"sourceCode":"    const size = buf.readUInt32LE(offset + 4);\n    if (id === \"fmt \") {\n      head.format = buf.readUInt16LE(offset + 8);\n      head.channels = buf.readUInt16LE(offset + 10);\n      head.sampleRate = buf.readUInt32LE(offset + 12);\n      head.bits = buf.readUInt16LE(offset + 22);\n    } else if (id === \"data\") {\n      data = buf.subarray(offset + 8, Math.min(buf.length, offset + 8 + size));\n      break;\n    }\n    offset += 8 + size + (size % 2);\n  }\n  return { ...head, data };\n}\n\nexport function readWav(path: string): WavData {\n  const buf = readFileSync(path);\n  if (buf.length < 44 || buf.toString(\"ascii\", 0, 4) !== \"RIFF\") {\n    throw new AudioFxRenderError(`Not a WAV file: ${path}`);\n  }\n  const { format, channels, sampleRate, bits, data } = readWavChunks(buf);\n  if (!data) throw new AudioFxRenderError(`WAV has no data chunk: ${path}`);\n  return { samples: decodeSamples(data, format, bits, path), sampleRate, channels };\n}\n\n/** Interleaved samples as floats, for the two formats the mixer emits upstream. */\nfunction decodeSamples(data: Buffer, format: number, bits: number, path: string): Float32Array {\n  if (format === 3 && bits === 32) {\n    const n = Math.floor(data.length / 4);\n    // A Float32Array view demands a 4-aligned offset, and chunk layouts that put\n    // `data` on an odd boundary (an 18-byte fmt plus a fact chunk, which\n    // ffmpeg's pcm_f32le writes) would otherwise throw RangeError. Copy then.\n    if (data.byteOffset % 4 === 0) return new Float32Array(data.buffer, data.byteOffset, n);\n    const copied = new Float32Array(n);\n    for (let i = 0; i < n; i++) copied[i] = data.readFloatLE(i * 4);\n    return copied;\n  }","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/engine/src/services/audioFxRender.ts#L54-L90","documentation":"readWav() reads a file synchronously and rejects it as `Not a WAV file` if it is shorter than 44 bytes (too small to contain even the canonical header) or its first four bytes are not the ASCII `RIFF` magic. This is the minimal sanity gate before walking WAV chunks; only files the engine's own mixer/extract pipeline emits are supported.","triggerScenarios":"Calling readWav(path) on a non-WAV file (MP3, AAC, FLAC, OGG), a truncated/empty file, a RIFX (big-endian) WAV, or a file written with a non-RIFF container. The audio FX render step hands it the WAV produced upstream by the mixer; pointing it at anything else triggers this.","commonSituations":"An upstream audio step wrote MP3/AAC instead of WAV (codec mismatch); a render was interrupted leaving a 0-byte WAV; a user dropped an .mp3 renamed to .wav; the source asset is big-endian RIFX from an uncommon encoder.","solutions":["Confirm the file is actually WAV PCM/float: `file <path>` or inspect the first bytes (`xxd <path> | head -1`) — expect `RIFF`.","If it is a different format, convert upstream: `ffmpeg -i in.mp3 -c:a pcm_s16le out.wav` (16-bit PCM) or `pcm_f32le` for float.","If the file is empty/truncated, re-run the upstream render/mix step that produced it.","Ensure readWav is only called on files produced by the engine's own mixer/extract pipeline."],"exampleFix":"# before: source is mp3 mislabeled\n$ file track.wav  # -> MPEG ADTS\n\n# after: transcode to the WAV the mixer emits\n$ ffmpeg -i track.wav -c:a pcm_s16le -ar 48000 -ac 2 track.pcm.wav","handlingStrategy":"type-guard","validationCode":"import { readFileSync, statSync } from \"node:fs\";\n\nfunction isWavFile(path: string): boolean {\n  let buf;\n  try { buf = readFileSync(path); } catch { return false; }\n  return buf.length >= 44 && buf.toString(\"ascii\", 0, 4) === \"RIFF\";\n}\n\nif (!isWavFile(path)) throw new Error(`not a WAV file: ${path}`);","typeGuard":"import { readFileSync } from \"node:fs\";\n\nfunction isPcmWav(path: string): boolean {\n  try {\n    const b = readFileSync(path, { start: 0, end: 44 });\n    return b.toString(\"ascii\", 0, 4) === \"RIFF\" && b.toString(\"ascii\", 8, 12) === \"WAVE\";\n  } catch { return false; }\n}","tryCatchPattern":"import { AudioFxRenderError } from \"@hyperframes/engine/services/audioFxRender\";\n\ntry {\n  return readWav(path);\n} catch (e) {\n  if (e instanceof AudioFxRenderError && /Not a WAV file/.test(e.message)) {\n    throw new Error(`upstream produced a non-WAV audio asset at ${path}; check the mixer/extract step`, { cause: e });\n  }\n  throw e;\n}","preventionTips":["Only feed readWav files produced by the engine's mixer/extract pipeline (16-bit PCM or 32-bit float WAV).","Run `file <path>` and confirm `RIFF` before invoking.","Convert foreign audio with `ffmpeg -i in -c:a pcm_s16le out.wav` upstream."],"tags":["audio","wav","audio-fx-render","file-format"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}