{"record":{"id":"e9298bd83883671e","repo":"heygen-com/hyperframes","slug":"wav-has-no-data-chunk-path","errorCode":null,"errorMessage":"WAV has no data chunk: ${path}","messagePattern":"WAV has no data chunk: (.+?)","errorType":"exception","errorClass":"AudioFxRenderError","httpStatus":null,"severity":"error","filePath":"packages/engine/src/services/audioFxRender.ts","lineNumber":75,"sourceCode":"      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  }\n  if (format === 1 && bits === 16) {\n    const n = Math.floor(data.length / 2);\n    const out = new Float32Array(n);","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/engine/src/services/audioFxRender.ts#L57-L93","documentation":"readWav() found a valid `RIFF` header but readWavChunks() walked the chunk list without encountering a `data` chunk. The WAV is structurally incomplete: it has a RIFF container (and possibly a `fmt ` chunk) but no PCM payload to decode. The reader refuses to guess rather than emit silence.","triggerScenarios":"A WAV with only a `fmt ` chunk and no `data` chunk (some metadata extractors emit these), a truncated file whose `data` chunk was never written, a file whose chunk IDs are non-standard so the walker skips the payload, or a file where the `data` chunk sits beyond EOF due to truncation.","commonSituations":"An interrupted encode that wrote the header but not the samples; a metadata-only WAV exported by a tag editor; an upstream FFmpeg run killed mid-write; a file copied incompletely (rsync interrupted).","solutions":["Inspect the chunk layout: `ffprobe <path>` should report a PCM stream with a non-zero duration; if duration is N/A, the data chunk is missing or empty.","Re-run the upstream encode/mix step that produced the WAV so it completes and flushes the data chunk.","If the file is a metadata stub, regenerate the actual audio asset.","Verify file size matches expected duration × sampleRate × channels × bits/8."],"exampleFix":"# before\n$ ffprobe broken.wav   # -> Duration: N/A, no audio stream\n\n# after: re-encode so the data chunk is written\n$ ffmpeg -i source.mov -c:a pcm_s16le -ar 48000 -ac 2 broken.wav\n$ ffprobe broken.wav   # -> Duration: 00:00:12.00, pcm_s16le","handlingStrategy":"try-catch","validationCode":"import { readFileSync } from \"node:fs\";\n\nfunction wavHasDataChunk(path: string): boolean {\n  let buf;\n  try { buf = readFileSync(path); } catch { return false; }\n  if (buf.length < 12 || buf.toString(\"ascii\", 0, 4) !== \"RIFF\") return false;\n  let off = 12;\n  while (off + 8 <= buf.length) {\n    if (buf.toString(\"ascii\", off, off + 4) === \"data\") return true;\n    const size = buf.readUInt32LE(off + 4);\n    off += 8 + size + (size % 2);\n  }\n  return false;\n}\n\nif (!wavHasDataChunk(path)) throw new Error(`WAV missing data chunk: ${path}`);","typeGuard":"function isCompleteWav(path: string): boolean {\n  return wavHasDataChunk(path); // same helper as validationCode\n}","tryCatchPattern":"import { AudioFxRenderError } from \"@hyperframes/engine/services/audioFxRender\";\n\ntry {\n  return readWav(path);\n} catch (e) {\n  if (e instanceof AudioFxRenderError && /no data chunk/.test(e.message)) {\n    throw new Error(`audio asset ${path} is incomplete (no data chunk); re-run the upstream encode`, { cause: e });\n  }\n  throw e;\n}","preventionTips":["Confirm `ffprobe <path>` reports a non-N/A duration before reading.","Re-run interrupted upstream encodes rather than retrying readWav on the partial file.","Verify file size is at least duration × sampleRate × channels × bits/8."],"tags":["audio","wav","audio-fx-render","corrupt-file","data-chunk"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}