{"record":{"id":"e3da3f33d34fc6b9","repo":"didi/DoKit","slug":"the-duration-is-less-than-0","errorCode":null,"errorMessage":"The duration is less than 0.","messagePattern":"The duration is less than 0\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"Android/dokit-util/src/main/java/com/didichuxing/doraemonkit/util/DebouncingUtils.java","lineNumber":65,"sourceCode":"     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isValid(@NonNull final View view, final long duration) {\n        return isValid(String.valueOf(view.hashCode()), duration);\n    }\n\n    /**\n     * Return whether the key is not in a jitter state.\n     *\n     * @param key      The key.\n     * @param duration The duration.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isValid(@NonNull String key, final long duration) {\n        if (TextUtils.isEmpty(key)) {\n            throw new IllegalArgumentException(\"The key is null.\");\n        }\n        if (duration < 0) {\n            throw new IllegalArgumentException(\"The duration is less than 0.\");\n        }\n        long curTime = SystemClock.elapsedRealtime();\n        clearIfNecessary(curTime);\n        Long validTime = KEY_MILLIS_MAP.get(key);\n        if (validTime == null || curTime >= validTime) {\n            KEY_MILLIS_MAP.put(key, curTime + duration);\n            return true;\n        }\n        return false;\n    }\n\n    private static void clearIfNecessary(long curTime) {\n        if (KEY_MILLIS_MAP.size() < CACHE_SIZE) return;\n        for (Iterator<Map.Entry<String, Long>> it = KEY_MILLIS_MAP.entrySet().iterator(); it.hasNext(); ) {\n            Map.Entry<String, Long> entry = it.next();\n            Long validTime = entry.getValue();\n            if (curTime >= validTime) {\n                it.remove();","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit-util/src/main/java/com/didichuxing/doraemonkit/util/DebouncingUtils.java#L47-L83","documentation":"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.","triggerScenarios":"Passing a negative duration constant: isValid(key, -1); durations parsed from config (remote config, JSON) without validation; using -1 as a 'disable debounce' sentinel.","commonSituations":"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.","solutions":["Pass 0 to make every call valid immediately (no debounce) instead of a negative value.","Sanitize config values: duration = Math.max(0, configDuration).","Validate parsed durations at load time and use an explicit sentinel/boolean rather than a negative number."],"exampleFix":"// before\nlong d = remoteConfig.getLong(\"debounce_ms\", -1);\nDebouncingUtils.isValid(key, d); // throws when config missing\n\n// after\nlong d = Math.max(0, remoteConfig.getLong(\"debounce_ms\", 0));\nDebouncingUtils.isValid(key, d);","handlingStrategy":"validation","validationCode":"long safeDuration = Math.max(0, duration);\nboolean valid = DebouncingUtils.isValid(key, safeDuration);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use 0 (not -1) to disable debouncing.","Validate remote-config durations at load time."],"tags":["debounce","validation","config"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}