airbnb/epoxy · error · IllegalStateException

Notifications already paused

Error message

Notifications already paused

What it means

ModelList.pauseNotifications is internal and idempotency-guarded: if notifications are already paused it throws IllegalStateException. This prevents double-pausing (typically two nested batchUpdate calls in Epoxy's controller/diffing machinery) from unbalancing pause/resume pairs.

Solutions

  1. Ensure pause/resume are strictly paired (pause once, resume once)
  2. Avoid calling setModels or mutating the list from inside change notifications
  3. Synchronize list updates to a single thread
  4. Do not call internal ModelList.pauseNotifications from app code

Example fix

// before
list.pauseNotifications();
list.pauseNotifications(); // throws
// after
if (!isPaused()) { list.pauseNotifications(); }
try { /* mutate */ } finally { list.resumeNotifications(); }
Defensive patterns

Strategy: try-catch

Try / catch

try { list.pauseNotifications(); } catch (IllegalStateException e) { Log.w(TAG, "already paused", e); }

Prevention

When it happens

Trigger: Calling pauseNotifications() when notificationsPaused is already true — usually two overlapping batch updates on the same ModelList.

Common situations: Re-entrant model builds/updates (setModels called inside a notification callback); concurrent threads touching the same controller's model list; misuse of internal APIs.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13). Data as JSON: /api/errors/4e6b1bba6a4fc322. Report an issue: GitHub.

Appendix: source

Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/ModelList.java:39

  ModelList(int expectedModelCount) {
    super(expectedModelCount);
  }

  ModelList() {

  }

  interface ModelListObserver {
    void onItemRangeInserted(int positionStart, int itemCount);
    void onItemRangeRemoved(int positionStart, int itemCount);
  }

  private boolean notificationsPaused;
  private ModelListObserver observer;

  void pauseNotifications() {
    if (notificationsPaused) {
      throw new IllegalStateException("Notifications already paused");
    }
    notificationsPaused = true;
  }

  void resumeNotifications() {
    if (!notificationsPaused) {
      throw new IllegalStateException("Notifications already resumed");
    }
    notificationsPaused = false;
  }

  void setObserver(ModelListObserver observer) {
    this.observer = observer;
  }

  private void notifyInsertion(int positionStart, int itemCount) {
    if (!notificationsPaused && observer != null) {
      observer.onItemRangeInserted(positionStart, itemCount);

View on GitHub (pinned to e45bd3a61f)