google/ExoPlayer · error · java.util.concurrent.TimeoutException
TimestampAdjuster failed to initialize in " + timeoutMs + "
Error message
TimestampAdjuster failed to initialize in " + timeoutMs + " milliseconds
What it means
TimestampAdjuster.awaitInitializedBlocksOrTimeout (via initialize or getFirstSampleTimestampUs paths) throws java.util.concurrent.TimeoutException when the adjuster is not initialized within the caller-supplied timeoutMs: the wait loop accumulates elapsed realtime via wait(remainingTimeoutMs) and, on total wait >= timeoutMs with isInitialized() still false, throws with the timeout in the message. Initialization happens when the first 'reference' sample timestamp is set (usually the first frame after a seek or stream start); until then all other threads block.
Source
Thrown at library/common/src/main/java/com/google/android/exoplayer2/util/TimestampAdjuster.java:143
return;
} else if (canInitialize) {
this.nextSampleTimestampUs.set(nextSampleTimestampUs);
} else {
// Wait for another calling thread to complete initialization.
long totalWaitDurationMs = 0;
long remainingTimeoutMs = timeoutMs;
while (!isInitialized()) {
if (timeoutMs == 0) {
wait();
} else {
checkState(remainingTimeoutMs > 0);
long waitStartingTimeMs = SystemClock.elapsedRealtime();
wait(remainingTimeoutMs);
totalWaitDurationMs += SystemClock.elapsedRealtime() - waitStartingTimeMs;
if (totalWaitDurationMs >= timeoutMs && !isInitialized()) {
String message =
"TimestampAdjuster failed to initialize in " + timeoutMs + " milliseconds";
throw new TimeoutException(message);
}
remainingTimeoutMs = timeoutMs - totalWaitDurationMs;
}
}
}
}
/**
* Returns the value of the first adjusted sample timestamp in microseconds, or {@link
* C#TIME_UNSET} if timestamps will not be offset or if the adjuster is in shared mode.
*/
public synchronized long getFirstSampleTimestampUs() {
return firstSampleTimestampUs == MODE_NO_OFFSET || firstSampleTimestampUs == MODE_SHARED
? C.TIME_UNSET
: firstSampleTimestampUs;
}
/**View on GitHub (pinned to dd430f7053)
Solutions
- Increase or disable the timeout: construct TimestampAdjuster with a larger/0 timeout (0 = wait indefinitely), or the media3 API that exposes the initialization timeout for the shared-timestamp-adjuster path
- Ensure the owner/first track actually produces a sample: check upstream MediaSource errors, decoder init exceptions, and that the seek position maps to a real sample (SeekMap)
- Release order: when stopping playback before first frame, release renderers so waiting threads are interrupted rather than timing out
- For custom pipeline code, set the initial timestamp explicitly via TimestampAdjuster.reset with a first sample or use setTimestampOffsetSync so waiters never block on first data
Example fix
// before TimestampAdjuster adjuster = new TimestampAdjuster(TIMEOUT_MS); // short timeout, blocks timeout long base = adjuster.getFirstSampleTimestampUs(); // after TimestampAdjuster adjuster = new TimestampAdjuster(0 /* wait indefinitely */); long base = adjuster.getFirstSampleTimestampUs(); // or feed owner sample promptly
Defensive patterns
Strategy: retry
Validate before calling
if (!adjuster.isInitialized()) {
// ensure the owner track will deliver a sample, or initialize synchronously
} Try / catch
try { adjuster.getFirstSampleTimestampUs(); }
catch (TimeoutException e) { /* release renderers / retry stream init / surface decoder error */ } Prevention
- Feed the owner track's first sample promptly; don't leave shared adjusters uninitialized across seeks
- Choose timeout 0 (wait indefinitely) when startup latency is unbounded, or a generous device-scaled timeout
- On release, tear down waiting threads instead of letting them hit the timeout
When it happens
Trigger: Shared-mode TimestampAdjuster groups (SPMC video+audio): the thread that should call adjustSampleTimestamp first (owner) never does because its decoder stalled, the seek target track never produced a frame, or the owner's renderer was disabled; dependent tracks then time out after their configured timeoutMs (passed via DefaultRenderersFactory / setEnableDecoderFallback paths or experimental settings).
Common situations: Live streams where the initial segment lacks the expected track; DRM/decoder init failure on the owner track; content with audio-only or video-only periods where the other adjuster never initializes; aggressive timeouts set by an app (e.g. via DefaultRenderersFactory's timestamp adjuster initialization timeout) interacting with slow device decoders; blocked threads at app shutdown if release happens before first sample.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Message delivery timed out.
- Attempting to open a session using a dummy ExoMediaDrm.
- NO_SUITABLE_DECODER_ERROR
AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14).
Data as JSON: /api/errors/3f9c4c51fa5f6c8a.
Report an issue: GitHub.