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

  1. Increase timeoutMs to comfortably exceed the slowest legitimate operation (e.g. codec init or seek, commonly several seconds).
  2. Never call blockUntilDelivered on the application/playback thread and never while holding locks the playback thread needs.
  3. Prefer the async alternative: pass a PlayerMessage.Target and react in onMessageCompleted instead of blocking.
  4. 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

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

Related errors


AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14). Data as JSON: /api/errors/66d84e64a3782edb. Report an issue: GitHub.