libgdx/libgdx · error · GdxRuntimeException
Error while preloading MP3
Error message
Error while preloading MP3
What it means
Thrown by Mp3.Music's constructor when the first readFrame() raises a javazoom.jl.decoder.BitstreamException — JLayer's parser choked on the bitstream before a frame could even be examined (the null-header 'Empty MP3' case is separate). The cause is chained so the underlying parse error (e.g. INVALID_FRAME, BITSTREAM_LAST) is visible.
Source
Thrown at backends/gdx-backend-lwjgl/src/com/badlogic/gdx/backends/lwjgl/audio/Mp3.java:52
private Bitstream bitstream;
private OutputBuffer outputBuffer;
private MP3Decoder decoder;
public Music (OpenALLwjglAudio audio, FileHandle file) {
super(audio, file);
if (audio.noDevice) return;
bitstream = new Bitstream(file.read());
decoder = new MP3Decoder();
try {
Header header = bitstream.readFrame();
if (header == null) throw new GdxRuntimeException("Empty MP3");
int channels = header.mode() == Header.SINGLE_CHANNEL ? 1 : 2;
outputBuffer = new OutputBuffer(channels, false);
decoder.setOutputBuffer(outputBuffer);
setup(channels, 16, header.getSampleRate());
} catch (BitstreamException e) {
throw new GdxRuntimeException("Error while preloading MP3", e);
}
}
public int read (byte[] buffer) {
try {
boolean setup = bitstream == null;
if (setup) {
bitstream = new Bitstream(file.read());
decoder = new MP3Decoder();
}
int totalLength = 0;
int minRequiredLength = buffer.length - OutputBuffer.BUFFERSIZE * 2;
while (totalLength <= minRequiredLength) {
Header header = bitstream.readFrame();
if (header == null) break;
if (setup) {
int channels = header.mode() == Header.SINGLE_CHANNEL ? 1 : 2;View on GitHub (pinned to 97f4086187)
Solutions
- Re-obtain the MP3 from a trusted source and verify integrity (checksum or re-encode via ffmpeg)
- Strip oversized/complex ID3 tags (ffmpeg -i in.mp3 -c copy -map_metadata -1 out.mp3) to rule out tag parsing issues
- If files are fetched over the network at runtime, download fully before calling newMusic (or check Content-Length)
- Catch GdxRuntimeException around Gdx.audio.newMusic and skip/fallback the track so one bad asset doesn't kill the app
Example fix
# before # track.mp3 truncated at 30% -> BitstreamException -> "Error while preloading MP3" # after ffmpeg -i track.mp3 -c copy -map_metadata -1 track_clean.mp3 # full re-encode if still failing: ffmpeg -i track.mp3 -codec:a libmp3lame -qscale:a 4 track_clean.mp3
Defensive patterns
Strategy: try-catch
Validate before calling
// cheap sanity check before loading
FileHandle f = Gdx.files.internal("s.mp3");
boolean plausible = f.length() > 4096;
// full validation requires decoding; use a tool at build time instead (ffmpeg -v error) Try / catch
try {
music = Gdx.audio.newMusic(f);
} catch (GdxRuntimeException e) {
if (e.getCause() instanceof javazoom.jl.decoder.BitstreamException) {
Gdx.app.error("Audio", "corrupt mp3: " + f, e);
}
} Prevention
- Run ffmpeg -v error -i asset.mp3 -f null - over audio assets in CI
- Strip heavy ID3 artwork tags from game assets
- Ship OGG where possible; the JLayer MP3 path is the most fragile
When it happens
Trigger: new Music(...) where file.read() yields data that breaks JLayer's bitstream parser: truncated mid-header files, corrupted ID3v2 tags with wrong sizes, or non-MP3 bytes.
Common situations: Partially downloaded/streamed MP3s; assets corrupted in transfer (FTP text mode); large ID3v2 tags with embedded art that JLayer 1.0.1 mishandles.
Related errors
AI-assisted analysis of libgdx/libgdx@97f4086187 (2026-08-14).
Data as JSON: /api/errors/e1f71cffcd88d049.
Report an issue: GitHub.