material-components/material-components-android · error · IllegalArgumentException

STATE_DRAGGING should not be set externally.

Error message

STATE_DRAGGING should not be set externally.

What it means

BottomSheetBehavior.setState(int) requests a transition to a target state. STATE_DRAGGING and STATE_SETTLING are transient states driven internally by touch events and the drag helper; they cannot be requested externally. Passing either one throws IllegalArgumentException with the offending state name in the message.

Source

Thrown at lib/java/com/google/android/material/bottomsheet/BottomSheetBehavior.java:1478

  /**
   * Removes a previously added callback.
   *
   * @param callback The callback to remove.
   */
  public void removeBottomSheetCallback(@NonNull BottomSheetCallback callback) {
    callbacks.remove(callback);
  }

  /**
   * Sets the state of the bottom sheet. The bottom sheet will transition to that state with
   * animation.
   *
   * @param state One of {@link #STATE_COLLAPSED}, {@link #STATE_EXPANDED}, {@link #STATE_HIDDEN},
   *     or {@link #STATE_HALF_EXPANDED}.
   */
  public void setState(@StableState int state) {
    if (state == STATE_DRAGGING || state == STATE_SETTLING) {
      throw new IllegalArgumentException(
          "STATE_"
              + (state == STATE_DRAGGING ? "DRAGGING" : "SETTLING")
              + " should not be set externally.");
    }
    if (!hideable && state == STATE_HIDDEN) {
      Log.w(TAG, "Cannot set state: " + state);
      return;
    }
    final int finalState;
    if (state == STATE_HALF_EXPANDED
        && fitToContents
        && getTopOffsetForState(state) <= fitToContentsOffset) {
      // Skip to the expanded state if we would scroll past the height of the contents.
      finalState = STATE_EXPANDED;
    } else {
      finalState = state;
    }
    if (viewRef == null || viewRef.get() == null) {

View on GitHub (pinned to ac7e18efee)

Solutions

  1. Only call setState with stable states: STATE_COLLAPSED, STATE_EXPANDED, STATE_HALF_EXPANDED, or STATE_HIDDEN.
  2. When persisting state, map transient states to their nearest stable equivalent before saving (e.g. SETTLING -> EXPANDED).
  3. Filter DRAGGING/SETTLING in any callback-forwarding code before re-invoking setState.

Example fix

// before
behavior.setBottomSheetCallback(new BottomSheetCallback() {
  @Override public void onStateChanged(View sheet, int newState) {
    savedState = newState; // may be STATE_DRAGGING / STATE_SETTLING
  } ...
});
behavior.setState(savedState); // throws if transient

// after
private static int toStable(int s) {
  return (s == STATE_DRAGGING || s == STATE_SETTLING) ? STATE_EXPANDED : s;
}
behavior.setState(toStable(savedState));
Defensive patterns

Strategy: validation

Validate before calling

private static boolean isStableSheetState(int s) {
  return s == BottomSheetBehavior.STATE_COLLAPSED
      || s == BottomSheetBehavior.STATE_EXPANDED
      || s == BottomSheetBehavior.STATE_HALF_EXPANDED
      || s == BottomSheetBehavior.STATE_HIDDEN;
}
if (isStableSheetState(requestedState)) behavior.setState(requestedState);

Prevention

When it happens

Trigger: Calling setState(BottomSheetBehavior.STATE_DRAGGING) or setState(BottomSheetBehavior.STATE_SETTLING) directly; forwarding a value received via a BottomSheetCallback onStateChanged callback back into setState (callbacks legitimately report DRAGGING/SETTLING).

Common situations: Persisting the last reported state and restoring it later with setState — if the app was killed mid-drag the saved state was SETTLING/DRAGGING; 'echo' logic that mirrors state changes between two sheets; switching on state ints and falling into a default branch that calls setState(state).

Related errors


AI-assisted analysis of material-components/material-components-android@ac7e18efee (2026-08-14). Data as JSON: /api/errors/07c2b18527e8c573. Report an issue: GitHub.