airbnb/epoxy · error · IllegalArgumentException

Unknown type

Error message

Unknown type: {op.type}

What it means

notifyChanges() iterates diff-generated operations and switches on each op's type; a type outside the expected set falls into the default branch and throws IllegalArgumentException("Unknown type: ..."). This is an internal invariant: the op list built by createOperationList should only contain known types, so hitting this indicates corrupted or externally-constructed operation data.

Solutions

  1. Use an unmodified stock version of DiffHelper/Epoxy (no forks)
  2. Align library versions: ensure epoxy-adapter artifacts are all the same version
  3. File a bug with a repro if it occurs on stock code — it is an internal invariant violation
  4. Avoid constructing or mutating DiffOperation objects via reflection in tests
Defensive patterns

Strategy: try-catch

Try / catch

try {
  adapter.notifyModelsChanged();
} catch (IllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("Unknown type")) {
    Log.e(TAG, "DiffHelper internal op error", e);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling notifyModelChanges() when DiffHelper's internal operation list contains an unrecognized op type — effectively only possible with modified/interfered internal state, since ops are built internally from the diff.

Common situations: Custom forks of DiffHelper that added new op types without updating the switch; reflection-based tests constructing fake ops; a library version mismatch where a patched DiffHelper interacts with mismatched op constants.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/DiffHelper.java:163

        case UpdateOp.ADD:
          adapter.notifyItemRangeInserted(op.positionStart, op.itemCount);
          break;
        case UpdateOp.MOVE:
          adapter.notifyItemMoved(op.positionStart, op.itemCount);
          break;
        case UpdateOp.REMOVE:
          adapter.notifyItemRangeRemoved(op.positionStart, op.itemCount);
          break;
        case UpdateOp.UPDATE:
          if (immutableModels && op.payloads != null) {
            adapter.notifyItemRangeChanged(op.positionStart, op.itemCount,
                new DiffPayload(op.payloads));
          } else {
            adapter.notifyItemRangeChanged(op.positionStart, op.itemCount);
          }
          break;
        default:
          throw new IllegalArgumentException("Unknown type: " + op.type);
      }
    }
  }

  /**
   * Create a list of operations that define the difference between {@link #oldStateList} and {@link
   * #currentStateList}.
   */
  private UpdateOpHelper buildDiff(UpdateOpHelper updateOpHelper) {
    prepareStateForDiff();

    // The general approach is to first search for removals, then additions, and lastly changes.
    // Focusing on one type of operation at a time makes it easy to coalesce batch changes.
    // When we identify an operation and add it to the
    // result list we update the positions of items in the oldStateList to reflect
    // the change, this way subsequent operations will use the correct, updated positions.
    collectRemovals(updateOpHelper);

View on GitHub (pinned to e45bd3a61f)