didi/DoKit · error · IllegalArgumentException

The duration is less than 0.

Error message

The duration is less than 0.

What it means

The second guard in DebouncingUtils.isValid: the debounce window duration must be >= 0 because it is added to SystemClock.elapsedRealtime() to compute the expiry timestamp stored in KEY_MILLIS_MAP. A negative duration would create a timestamp in the past and break the 'validTime' semantics, so it is rejected up front.

Source

Thrown at Android/dokit-util/src/main/java/com/didichuxing/doraemonkit/util/DebouncingUtils.java:65

     * @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 626827cddb)

Solutions

  1. Pass 0 to make every call valid immediately (no debounce) instead of a negative value.
  2. Sanitize config values: duration = Math.max(0, configDuration).
  3. Validate parsed durations at load time and use an explicit sentinel/boolean rather than a negative number.

Example fix

// before
long d = remoteConfig.getLong("debounce_ms", -1);
DebouncingUtils.isValid(key, d); // throws when config missing

// after
long d = Math.max(0, remoteConfig.getLong("debounce_ms", 0));
DebouncingUtils.isValid(key, d);
Defensive patterns

Strategy: validation

Validate before calling

long safeDuration = Math.max(0, duration);
boolean valid = DebouncingUtils.isValid(key, safeDuration);

Prevention

When it happens

Trigger: Passing a negative duration constant: isValid(key, -1); durations parsed from config (remote config, JSON) without validation; using -1 as a 'disable debounce' sentinel.

Common situations: Remote-config-driven debounce intervals that default to -1 when the key is missing; time-unit confusion (passing 500 ms as -500 after sign arithmetic); feature flags that try to express 'no debounce' with a negative number.

Related errors


AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14). Data as JSON: /api/errors/e3da3f33d34fc6b9. Report an issue: GitHub.