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
- Only call setState with stable states: STATE_COLLAPSED, STATE_EXPANDED, STATE_HALF_EXPANDED, or STATE_HIDDEN.
- When persisting state, map transient states to their nearest stable equivalent before saving (e.g. SETTLING -> EXPANDED).
- 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
- Never echo onStateChanged values straight back into setState.
- Map transient states to stable ones before persisting.
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
- Invalid state to get top offset:
- ratio must be a float value between 0 and 1
- offset must be greater than or equal to 0
- The view is not a child of CoordinatorLayout
- The view is not associated with BottomSheetBehavior
AI-assisted analysis of material-components/material-components-android@ac7e18efee (2026-08-14).
Data as JSON: /api/errors/07c2b18527e8c573.
Report an issue: GitHub.