{"record":{"id":"66d84e64a3782edb","repo":"google/ExoPlayer","slug":"message-delivery-timed-out","errorCode":null,"errorMessage":"Message delivery timed out.","messagePattern":"Message delivery timed out\\.","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java","lineNumber":369,"sourceCode":"   * @throws TimeoutException If the {@code timeoutMs} elapsed and this message has not been\n   *     delivered and the player is still able to deliver the message.\n   * @throws InterruptedException If the current thread is interrupted while waiting for the message\n   *     to be delivered.\n   */\n  public synchronized boolean blockUntilDelivered(long timeoutMs)\n      throws InterruptedException, TimeoutException {\n    Assertions.checkState(isSent);\n    Assertions.checkState(looper.getThread() != Thread.currentThread());\n\n    long deadlineMs = clock.elapsedRealtime() + timeoutMs;\n    long remainingMs = timeoutMs;\n    while (!isProcessed && remainingMs > 0) {\n      clock.onThreadBlocked();\n      wait(remainingMs);\n      remainingMs = deadlineMs - clock.elapsedRealtime();\n    }\n    if (!isProcessed) {\n      throw new TimeoutException(\"Message delivery timed out.\");\n    }\n    return isDelivered;\n  }\n}\n","sourceCodeStart":351,"sourceCodeEnd":374,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java#L351-L374","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nplayer.createMessage(renderer).setType(MSG_SEEK).send().blockUntilDelivered(1_000);\n\n// after\nplayer.createMessage((messageType, payload) -> {\n      // runs on playback thread\n      doSeekWork(payload);\n    })\n    .setType(MSG_SEEK)\n    .setPayload(seekArg)\n    .setHandler(mainHandler)\n    .send()\n    .setListener((type, payload) -> uiCallback.onSeekDone(), mainHandler);","handlingStrategy":"try-catch","validationCode":"if (!message.isSent() || Looper.myLooper() == message.getLooper().getLooper()) {\n  throw new IllegalStateException(\"precondition violated for blockUntilDelivered\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  message.blockUntilDelivered(TimeUnit.SECONDS.toMillis(10));\n} catch (TimeoutException e) {\n  // not fatal: message may still be processed; verify state, do not assume\n  handlePossibleLateDelivery(message);\n} catch (InterruptedException e) {\n  Thread.currentThread().interrupt();\n}","preventionTips":["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()"],"tags":["exoplayer","playermessage","timeout","threading","messaging"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}