google/ExoPlayer · error · TimeoutException
Message delivery timed out.
Error message
Message delivery timed out.
What it means
PlayerMessage.blockUntilDelivered(timeoutMs) lets a caller on a non-playback thread wait for a message sent to a player component (renderer, loader) to be processed on the playback Looper. It asserts the message was sent and that the caller is not the playback thread itself, then waits up to timeoutMs; if the target never processes the message in time, a TimeoutException('Message delivery timed out.') is thrown. Common legitimate causes include the renderer being busy in a long codec or network operation, and blocked/delayed message dispatch after seek or release.
Source
Thrown at library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java:369
* @throws TimeoutException If the {@code timeoutMs} elapsed and this message has not been
* delivered and the player is still able to deliver the message.
* @throws InterruptedException If the current thread is interrupted while waiting for the message
* to be delivered.
*/
public synchronized boolean blockUntilDelivered(long timeoutMs)
throws InterruptedException, TimeoutException {
Assertions.checkState(isSent);
Assertions.checkState(looper.getThread() != Thread.currentThread());
long deadlineMs = clock.elapsedRealtime() + timeoutMs;
long remainingMs = timeoutMs;
while (!isProcessed && remainingMs > 0) {
clock.onThreadBlocked();
wait(remainingMs);
remainingMs = deadlineMs - clock.elapsedRealtime();
}
if (!isProcessed) {
throw new TimeoutException("Message delivery timed out.");
}
return isDelivered;
}
}
View on GitHub (pinned to dd430f7053)
Solutions
- Increase timeoutMs to comfortably exceed the slowest legitimate operation (e.g. codec init or seek, commonly several seconds).
- Never call blockUntilDelivered on the application/playback thread and never while holding locks the playback thread needs.
- Prefer the async alternative: pass a PlayerMessage.Target and react in onMessageCompleted instead of blocking.
- Catch TimeoutException and treat it as 'message may still complete later' — re-check player state rather than crashing; ensure release() is not racing the blocked call.
Example fix
// before
player.createMessage(renderer).setType(MSG_SEEK).send().blockUntilDelivered(1_000);
// after
player.createMessage((messageType, payload) -> {
// runs on playback thread
doSeekWork(payload);
})
.setType(MSG_SEEK)
.setPayload(seekArg)
.setHandler(mainHandler)
.send()
.setListener((type, payload) -> uiCallback.onSeekDone(), mainHandler); Defensive patterns
Strategy: try-catch
Validate before calling
if (!message.isSent() || Looper.myLooper() == message.getLooper().getLooper()) {
throw new IllegalStateException("precondition violated for blockUntilDelivered");
} Try / catch
try {
message.blockUntilDelivered(TimeUnit.SECONDS.toMillis(10));
} catch (TimeoutException e) {
// not fatal: message may still be processed; verify state, do not assume
handlePossibleLateDelivery(message);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} Prevention
- Prefer async listeners (setListener) over blocking waits
- Never block the application thread while the playback thread might wait on the app
- Budget timeoutMs above worst-case codec init/seek latency
- Do not call blockUntilDelivered concurrently with player.release()
When it happens
Trigger: Calling PlayerMessage.send().blockUntilDelivered(timeoutMs) from another thread while the playback thread is stalled in a renderer's onMessage, a very long initial load/seek, or the player was released concurrently so the Looper stopped servicing messages.
Common situations: Apps calling player.createMessage(...).send() then blockUntilDelivered with a short timeout; blocking the main thread waiting on the playback thread which is itself waiting on the app (deadlock); timeout shorter than legitimate codec warm-up or seek time.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Player is accessed on the wrong thread. Current thread: '%s'
- Player is accessed on the wrong thread. Current thread: '%s'
- Failed to load decoder native libraries.
- Missing implementation to handle COMMAND_SET_VOLUME
- Missing implementation to handle COMMAND_SET_DEVICE_VOLUME o
AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14).
Data as JSON: /api/errors/66d84e64a3782edb.
Report an issue: GitHub.