heygen-com/hyperframes · error · AudioFxRenderError

WAV has no data chunk: ${path}

Error message

WAV has no data chunk: ${path}

What it means

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.

Source

Thrown at packages/engine/src/services/audioFxRender.ts:75

      head.channels = buf.readUInt16LE(offset + 10);
      head.sampleRate = buf.readUInt32LE(offset + 12);
      head.bits = buf.readUInt16LE(offset + 22);
    } else if (id === "data") {
      data = buf.subarray(offset + 8, Math.min(buf.length, offset + 8 + size));
      break;
    }
    offset += 8 + size + (size % 2);
  }
  return { ...head, data };
}

export function readWav(path: string): WavData {
  const buf = readFileSync(path);
  if (buf.length < 44 || buf.toString("ascii", 0, 4) !== "RIFF") {
    throw new AudioFxRenderError(`Not a WAV file: ${path}`);
  }
  const { format, channels, sampleRate, bits, data } = readWavChunks(buf);
  if (!data) throw new AudioFxRenderError(`WAV has no data chunk: ${path}`);
  return { samples: decodeSamples(data, format, bits, path), sampleRate, channels };
}

/** Interleaved samples as floats, for the two formats the mixer emits upstream. */
function decodeSamples(data: Buffer, format: number, bits: number, path: string): Float32Array {
  if (format === 3 && bits === 32) {
    const n = Math.floor(data.length / 4);
    // A Float32Array view demands a 4-aligned offset, and chunk layouts that put
    // `data` on an odd boundary (an 18-byte fmt plus a fact chunk, which
    // ffmpeg's pcm_f32le writes) would otherwise throw RangeError. Copy then.
    if (data.byteOffset % 4 === 0) return new Float32Array(data.buffer, data.byteOffset, n);
    const copied = new Float32Array(n);
    for (let i = 0; i < n; i++) copied[i] = data.readFloatLE(i * 4);
    return copied;
  }
  if (format === 1 && bits === 16) {
    const n = Math.floor(data.length / 2);
    const out = new Float32Array(n);

View on GitHub (pinned to c2996c8626)

Solutions

  1. 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.
  2. Re-run the upstream encode/mix step that produced the WAV so it completes and flushes the data chunk.
  3. If the file is a metadata stub, regenerate the actual audio asset.
  4. Verify file size matches expected duration × sampleRate × channels × bits/8.

Example fix

# before
$ ffprobe broken.wav   # -> Duration: N/A, no audio stream

# after: re-encode so the data chunk is written
$ ffmpeg -i source.mov -c:a pcm_s16le -ar 48000 -ac 2 broken.wav
$ ffprobe broken.wav   # -> Duration: 00:00:12.00, pcm_s16le
Defensive patterns

Strategy: try-catch

Validate before calling

import { readFileSync } from "node:fs";

function wavHasDataChunk(path: string): boolean {
  let buf;
  try { buf = readFileSync(path); } catch { return false; }
  if (buf.length < 12 || buf.toString("ascii", 0, 4) !== "RIFF") return false;
  let off = 12;
  while (off + 8 <= buf.length) {
    if (buf.toString("ascii", off, off + 4) === "data") return true;
    const size = buf.readUInt32LE(off + 4);
    off += 8 + size + (size % 2);
  }
  return false;
}

if (!wavHasDataChunk(path)) throw new Error(`WAV missing data chunk: ${path}`);

Type guard

function isCompleteWav(path: string): boolean {
  return wavHasDataChunk(path); // same helper as validationCode
}

Try / catch

import { AudioFxRenderError } from "@hyperframes/engine/services/audioFxRender";

try {
  return readWav(path);
} catch (e) {
  if (e instanceof AudioFxRenderError && /no data chunk/.test(e.message)) {
    throw new Error(`audio asset ${path} is incomplete (no data chunk); re-run the upstream encode`, { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/e9298bd83883671e. Report an issue: GitHub.