Blankj/AndroidUtilCode · error · IllegalArgumentException
The duration is less than 0.
Error message
The duration is less than 0.
What it means
DebouncingUtils.isValid(String, long) records curTime + duration as the 'valid-until' timestamp for a key. A negative duration would set a valid-until time in the past, instantly blocking every subsequent call, so the method rejects negative durations with IllegalArgumentException. Zero is allowed and means 'no debounce'.
Source
Thrown at lib/utilcode/src/main/java/com/blankj/utilcode/util/DebouncingUtils.java:64
* @return {@code true}: yes<br>{@code false}: no
*/
public static boolean isValid(@NonNull final View view, final long duration) {
return isValid(String.valueOf(view.hashCode()), duration);
}
/**
* Return whether the key is not in a jitter state.
*
* @param key The key.
* @param duration The duration.
* @return {@code true}: yes<br>{@code false}: no
*/
public static boolean isValid(@NonNull String key, final long duration) {
if (TextUtils.isEmpty(key)) {
throw new IllegalArgumentException("The key is null.");
}
if (duration < 0) {
throw new IllegalArgumentException("The duration is less than 0.");
}
long curTime = SystemClock.elapsedRealtime();
clearIfNecessary(curTime);
Long validTime = KEY_MILLIS_MAP.get(key);
if (validTime == null || curTime >= validTime) {
KEY_MILLIS_MAP.put(key, curTime + duration);
return true;
}
return false;
}
private static void clearIfNecessary(long curTime) {
if (KEY_MILLIS_MAP.size() < CACHE_SIZE) return;
for (Iterator<Map.Entry<String, Long>> it = KEY_MILLIS_MAP.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Long> entry = it.next();
Long validTime = entry.getValue();
if (curTime >= validTime) {
it.remove();View on GitHub (pinned to 7b4caf9e54)
Solutions
- Coerce the duration: use Math.max(0, duration) so negative/unset values become 'no debounce' instead of throwing.
- Translate a -1 'disabled' sentinel into 0 (or skip the call entirely) at the boundary where you read the config.
- Validate the duration at its source and reject/flag negative values there rather than inside isValid.
- If the duration is computed from timestamps, clamp the subtraction result to >= 0.
Example fix
// before boolean ok = DebouncingUtils.isValid(key, debounceMs); // debounceMs == -1 // after long duration = debounceMs < 0 ? 0 : debounceMs; boolean ok = DebouncingUtils.isValid(key, duration);
Defensive patterns
Strategy: validation
Validate before calling
// Validate duration before debouncing
long duration = debounceMs;
if (duration < 0) {
duration = 0; // or skip the call
}
boolean ok = DebouncingUtils.isValid(key, duration); Type guard
// Coerce duration to a valid value
public static long safeDebounce(long d) {
return Math.max(0, d);
} Try / catch
try {
boolean ok = DebouncingUtils.isValid(key, duration);
} catch (IllegalArgumentException e) {
// duration was negative; clamp and retry
ok = DebouncingUtils.isValid(key, Math.max(0, duration));
} Prevention
- Map a -1 'disabled' config sentinel to 0 or skip the call at the read boundary.
- Clamp computed durations (deadline - now) to >= 0.
- Zero duration means 'no debounce' — use it deliberately rather than negatives.
When it happens
Trigger: Calling isValid(key, duration) where duration is negative — typically an uninitialized long field defaulting through some path, an arithmetic underflow (elapsed - now when now > elapsed), or a config value of -1 used as a sentinel.
Common situations: Reading a debounce window from config with a -1 'disabled' sentinel and passing it straight through; computing duration as a deadline minus current time that goes negative when the deadline already passed; copy-pasting a timeout constant that is signed elsewhere.
Related errors
- The key is null.
- precision shouldn't be less than zero!
- byteSize shouldn't be less than zero!
- key must be between 1 and 256 bytes
- u can't instantiate me...
AI-assisted analysis of Blankj/AndroidUtilCode@7b4caf9e54 (2026-08-14).
Data as JSON: /api/errors/921d8b1155b22233.
Report an issue: GitHub.