{"record":{"id":"73f3a9e20730df41","repo":"microg/GmsCore","slug":"non-negative-loitering-delay-needs-to-be-set-when","errorCode":null,"errorMessage":"Non-negative loitering delay needs to be set when transition types include GEOFENCE_TRANSITION_DWELLING.","messagePattern":"Non-negative loitering delay needs to be set when transition types include GEOFENCE_TRANSITION_DWELLING\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"play-services-location/src/main/java/com/google/android/gms/location/Geofence.java","lineNumber":116,"sourceCode":"        private float radius;\n        private long expirationTime = Long.MIN_VALUE;\n        private int loiteringDelay = -1;\n        private int notificationResponsiveness;\n        private String requestId;\n        private @TransitionTypes int transitionTypes;\n\n        /**\n         * Creates a geofence object.\n         *\n         * @throws IllegalArgumentException if any parameters are not set or out of range\n         */\n        public Geofence build() throws IllegalArgumentException {\n            if (requestId == null) {\n                throw new IllegalArgumentException(\"Request ID not set.\");\n            } else if (transitionTypes == 0) {\n                throw new IllegalArgumentException(\"Transition types not set.\");\n            } else if ((transitionTypes & GEOFENCE_TRANSITION_DWELL) > 0 && loiteringDelay < 0) {\n                throw new IllegalArgumentException(\"Non-negative loitering delay needs to be set when transition types include GEOFENCE_TRANSITION_DWELLING.\");\n            } else if (expirationTime == Long.MIN_VALUE) {\n                throw new IllegalArgumentException(\"Expiration not set.\");\n            } else if (regionType == -1) {\n                throw new IllegalArgumentException(\"Geofence region not set.\");\n            } else if (notificationResponsiveness < 0) {\n                throw new IllegalArgumentException(\"Notification responsiveness should be nonnegative.\");\n            } else {\n                return new ParcelableGeofence(requestId, expirationTime, regionType, latitude, longitude, radius, transitionTypes, notificationResponsiveness, loiteringDelay);\n            }\n        }\n\n        /**\n         * Sets the region of this geofence. The geofence represents a circular area on a flat, horizontal plane.\n         *\n         * @param latitude  latitude in degrees, between -90 and +90 inclusive\n         * @param longitude longitude in degrees, between -180 and +180 inclusive\n         * @param radius    radius in meters\n         */","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-location/src/main/java/com/google/android/gms/location/Geofence.java#L98-L134","documentation":"When transition types include GEOFENCE_TRANSITION_DWELL, the builder requires a non-negative loitering delay, since dwell detection is defined by how long the user stays inside. If setLoiteringDelay was never called (default -1) or was set negative, build() throws this IllegalArgumentException.","triggerScenarios":"Calling build() after setTransitionTypes() included GEOFENCE_TRANSITION_DWELL but without calling setLoiteringDelay() with a value >= 0 (default loiteringDelay is -1).","commonSituations":"Adding DWELL to transition types later without adding the loitering delay; assuming a default dwell delay exists; migrating code from enter/exit-only fences to dwell fences.","solutions":["Call setLoiteringDelay(delayMillis) with a positive value (e.g. 30000 for 30s) before build()","Or remove GEOFENCE_TRANSITION_DWELL from setTransitionTypes() if dwell events are not needed"],"exampleFix":"// before\nbuilder.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_DWELL);\nGeofence fence = builder.build(); // throws\n// after\nbuilder.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_DWELL)\n       .setLoiteringDelay(30000);\nGeofence fence = builder.build();","handlingStrategy":"validation","validationCode":"if ((transitionTypes & Geofence.GEOFENCE_TRANSITION_DWELL) != 0 && loiteringDelayMillis <= 0) {\n    throw new IllegalStateException(\"DWELL transitions require setLoiteringDelay() > 0\");\n}","typeGuard":"static boolean dwellNeedsDelay(int transitionTypes, long loiteringDelay) { return (transitionTypes & Geofence.GEOFENCE_TRANSITION_DWELL) != 0 && loiteringDelay >= 0; }","tryCatchPattern":"try { return builder.build(); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"loitering\")) { builder.setLoiteringDelay(30000); return builder.build(); } throw e; }","preventionTips":["Whenever adding GEOFENCE_TRANSITION_DWELL, immediately add setLoiteringDelay() on the same line/chain","Pick a sensible default dwell delay (e.g. 30s) in your geofence factory"],"tags":["geofencing","validation","android","dwell"],"backgroundTag":"missing-required-argument","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}