Generated WAV downloads from `serve` contain placeholder RIFF/data chunk sizes
Summary
Hi! While using Pocket TTS through uvx pocket-tts serve, I noticed that downloaded/generated WAV files contain valid short PCM audio, but the WAV header keeps a placeholder-sized frame/data count.
The audio itself plays in tolerant players/editors, but strict readers may report the file as extremely long or malformed.
Environment
- OS: Windows
- Launch method:
uvx pocket-tts serve - Pocket TTS version:
2.1.0 - Runtime: uv/uvx-managed Python environment
- uv version:
uv 0.11.32 (3010295ae 2026-07-23 x86_64-pc-windows-msvc) - uvx version:
uvx 0.11.32 (3010295ae 2026-07-23 x86_64-pc-windows-msvc) - uvx-managed Python:
3.14.6 - Python executable:
C:\Users\Gebruiker\AppData\Local\\\uv\cache\archive-v0\tJQCxny2oyLPs0QZ\Scripts\python.exe - System Python: not installed / not on PATH
- Python launcher
py: not installed / not on PATH - Output path: local served web UI download
- Audio format observed: mono, 24 kHz, 16-bit PCM
- Voice mode: uploaded/custom voice prompt
Because system Python and the py launcher are not installed/on PATH, this appears to be running entirely through the uvx-managed Pocket TTS environment.
Observed behavior
Multiple generated WAV files have these header values:
RIFF chunk size: 2000000036
data chunk size: 2000000000
reported frame count: 1000000000For mono 16-bit PCM, 1_000_000_000 frames corresponds exactly to:
1,000,000,000 frames × 2 bytes = 2,000,000,000 bytesSo this looks like a streaming placeholder frame count being written into the saved WAV file.
Examples
Example 1:
file size: 94124 bytes
RIFF size in header: 2000000036
data chunk size in header: 2000000000
actual data bytes after data chunk header: 94080
format: mono, 24000 Hz, 16-bit PCM
actual duration from payload: ~1.96 seconds
reported duration from header: ~41666.67 secondsExample 2:
file size: 132524 bytes
RIFF size in header: 2000000036
data chunk size in header: 2000000000
actual data bytes after data chunk header: 132480
format: mono, 24000 Hz, 16-bit PCM
actual duration from payload: ~2.76 seconds
reported duration from header: ~41666.67 secondsExample 3:
file size: 94124 bytes
RIFF size in header: 2000000036
data chunk size in header: 2000000000
actual data bytes after data chunk header: 94080
format: mono, 24000 Hz, 16-bit PCM
actual duration from payload: ~1.96 seconds
reported duration from header: ~41666.67 secondsExpected behavior
Downloaded WAV files are readable as normal short WAV files by common audio tools.
Possible source
This appears related to the streaming WAV writer using a placeholder frame count:
self.wave_writer.setnframes(1_000_000_000)That value explains the observed 2_000_000_000 byte data chunk size for mono 16-bit output.
It also looks like header patching is intentionally disabled for unseekable streams:
# do not update the header for unseekable streams
self.wave_writer._patchheader = lambda: None
self.wave_writer.close()So the placeholder header is preserved in the downloaded WAV.
Minimal header inspection snippet
from pathlib import Path
import struct
import wave
path = Path("generated_speech.wav")
data = path.read_bytes()
riff_size = struct.unpack_from("<I", data, 4)[0]
data_pos = data.find(b"data")
data_size = struct.unpack_from("<I", data, data_pos + 4)[0]
print("file size:", len(data))
print("RIFF size:", riff_size)
print("data chunk size:", data_size)
with wave.open(str(path), "rb") as w:
print("params:", w.getparams())
print("duration from header:", w.getnframes() / w.getframerate())Impact
The audio payload appears usable, and rewriting the WAV header fixes the files locally.
I’m reporting this because the placeholder value seems to persist in saved downloads, and some tools may interpret the file duration from the header rather than the actual file length.
Possible fix direction
For a downloadable WAV response, maybe the server could either:
- buffer the complete audio and write a normal seekable WAV with corrected header sizes, or
- provide a separate non-streaming download route, or
- patch the header when the output target is seekable/finalized.
Thanks for the project. This was easy to work around locally once found, but I figured it was worth reporting because the placeholder value leaks into saved files in a reproducible way.
Source: kyutai-labs/pocket-tts