google/ExoPlayer · warning · PriorityTaskManager.PriorityTooLowException

Priority too low [priority=%d, highest=%d]

Error message

Priority too low [priority=%d, highest=%d]

What it means

PriorityTaskManager.proceedOrThrow(priority) throws PriorityTooLowException when the caller's priority does not exactly equal the manager's current highest_priority — meaning some other registered task is running at a higher priority and lower-priority work must not proceed. ExoPlayer uses this for codec/derasterizer vs network/download contention: network tasks call proceedOrThrow in a loop, backing off until their priority becomes the highest. It extends IOException, signaling a transient, retryable condition rather than a hard failure.

Source

Thrown at library/common/src/main/java/com/google/android/exoplayer2/util/PriorityTaskManager.java:106

   * @param priority The priority of the task.
   * @return Whether the task is allowed to proceed.
   */
  public boolean proceedNonBlocking(int priority) {
    synchronized (lock) {
      return highestPriority == priority;
    }
  }

  /**
   * A throwing variant of {@link #proceed(int)}.
   *
   * @param priority The priority of the task.
   * @throws PriorityTooLowException If the task is not allowed to proceed.
   */
  public void proceedOrThrow(int priority) throws PriorityTooLowException {
    synchronized (lock) {
      if (highestPriority != priority) {
        throw new PriorityTooLowException(priority, highestPriority);
      }
    }
  }

  /**
   * Unregister a task.
   *
   * @param priority The priority of the task.
   */
  public void remove(int priority) {
    synchronized (lock) {
      queue.remove(priority);
      highestPriority = queue.isEmpty() ? Integer.MIN_VALUE : Util.castNonNull(queue.peek());
      lock.notifyAll();
    }
  }
}

View on GitHub (pinned to dd430f7053)

Solutions

  1. Retry or block instead of failing: catch PriorityTooLowException and either wait/backoff-retry, or simply use proceed(priority) which blocks until the priority is highest — that is the intended API for long tasks
  2. Ensure priorities are consistent with registration: proceed/proceedOrThrow must be called with the same priority value the task registered with via add(priority)
  3. Re-check ordering around cancelation: always call remove(priority) in finally so highest_priority falls and other tasks can proceed
  4. For custom task types, register with C.PRIORITY_* constants that order sensibly vs playback so downloads yield but still run when idle

Example fix

// before
manager.proceedOrThrow(C.PRIORITY_BACKGROUND); // throws while playback active
// after
manager.proceed(C.PRIORITY_BACKGROUND); // blocks until allowed
try {
  // task body
} finally {
  manager.remove(C.PRIORITY_BACKGROUND);
}
Defensive patterns

Strategy: retry

Validate before calling

if (!manager.isRegisteredPriorityCurrent(priority)) { /* library-dependent; in practice: catch and retry */ }

Try / catch

while (true) {
  try { manager.proceedOrThrow(priority); break; }
  catch (PriorityTooLowException e) {
    // back off and retry, or switch to manager.proceed(priority) which blocks
  }
}

Prevention

When it happens

Trigger: A task registered at priority N calls proceedOrThrow(N) while playback (registered higher, e.g. C.PRIORITY_PLAYBACK) holds highest_priority; typical flow is PriorityTaskManager.proceed(N) which blocks, but code paths like CacheDataSource/HttpDataSource interruption handling call proceedOrThrow and expect PriorityTooLowException to pause downloading.

Common situations: Custom DownloadManager or CacheDataSource integrations where download priority (C.PRIORITY_BACKGROUND) is below active playback priority; multiple simultaneous streams re-prioritizing; calling proceedOrThrow while another thread's add() raised highest_priority between your add() and proceedOrThrow().

Related errors


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