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

  1. Re-obtain the MP3 from a trusted source and verify integrity (checksum or re-encode via ffmpeg)
  2. Strip oversized/complex ID3 tags (ffmpeg -i in.mp3 -c copy -map_metadata -1 out.mp3) to rule out tag parsing issues
  3. If files are fetched over the network at runtime, download fully before calling newMusic (or check Content-Length)
  4. 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

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.