microg/GmsCore · error · IllegalArgumentException
maxUpdateAgeMillis must be greater than or equal to 0, or IM
Error message
maxUpdateAgeMillis must be greater than or equal to 0, or IMPLICIT_MAX_UPDATE_AGE
What it means
setMaxUpdateAgeMillis accepts any non-negative age, plus the special sentinel IMPLICIT_MAX_UPDATE_AGE (-1) meaning 'no explicit max age'. Any other negative value is rejected with IllegalArgumentException. This keeps the sentinel convention intact while blocking meaningless negative durations.
Source
Thrown at play-services-location/src/main/java/com/google/android/gms/location/LocationRequest.java:723
if (intervalMillis < 0) throw new IllegalArgumentException("intervalMillis must be greater than or equal to 0");
this.intervalMillis = intervalMillis;
return this;
}
/**
* Sets the maximum age of an initial historical location delivered for this request. A value of 0 indicates that no initial
* historical location will be delivered, only freshly derived locations. A value {@link Long#MAX_VALUE} represents an effectively
* unbounded maximum age.
* <p>
* This may be set to the special value {@link #IMPLICIT_MAX_UPDATE_AGE} in which case the maximum update age will always be
* the same as the interval.
* <p>
* The default value is {@link #IMPLICIT_MAX_UPDATE_AGE}.
*/
@NonNull
public Builder setMaxUpdateAgeMillis(long maxUpdateAgeMillis) {
if (maxUpdateAgeMillis < 0 && maxUpdateAgeMillis != IMPLICIT_MAX_UPDATE_AGE)
throw new IllegalArgumentException("maxUpdateAgeMillis must be greater than or equal to 0, or IMPLICIT_MAX_UPDATE_AGE");
this.maxUpdateAgeMillis = maxUpdateAgeMillis;
return this;
}
/**
* Sets the longest a location update may be delayed. This parameter controls location batching behavior. If this is set to a
* value at least 2x larger than the interval specified by {@link #setIntervalMillis(long)}, then a device may (but is not required
* to) save power by delivering locations in batches. If clients do not require immediate delivery, consider setting this value
* as high as is reasonable to allow for additional power savings. When the {@link LocationRequest} is built, the maximum
* update delay will be set to the max of the provided maximum update delay and the interval. This normalizes requests
* without batching to have the maximum update delay equal to the interval.
* <p>
* For example, if a request is made with a 2s interval and a 10s maximum update delay, this implies that the device may
* choose to deliver batches of 5 locations every 10s (where each location in a batch represents a point in time ~2s after
* the previous).
* <p>
* Support for batching may vary by device hardware, so simply allowing batching via this parameter does not imply a client
* will receive batched results on all devices.View on GitHub (pinned to 157c9d86ac)
Solutions
- Use the documented sentinel: builder.setMaxUpdateAgeMillis(LocationRequest.Builder.IMPLICIT_MAX_UPDATE_AGE) to reset to the implicit default.
- Pass a non-negative age in milliseconds for an explicit maximum age, e.g. setMaxUpdateAgeMillis(10_000).
- Clamp or validate computed values: if (age < 0 && age != IMPLICIT_MAX_UPDATE_AGE) age = IMPLICIT_MAX_UPDATE_AGE;
Example fix
// before builder.setMaxUpdateAgeMillis(-1); // not the sentinel constant path // after builder.setMaxUpdateAgeMillis(LocationRequest.Builder.IMPLICIT_MAX_UPDATE_AGE); // or a value >= 0
Defensive patterns
Strategy: validation
Validate before calling
private static final long RESET_MAX_AGE = LocationRequest.Builder.IMPLICIT_MAX_UPDATE_AGE;
public static boolean isValidMaxUpdateAge(long ageMillis) {
return ageMillis >= 0 || ageMillis == LocationRequest.Builder.IMPLICIT_MAX_UPDATE_AGE;
} Type guard
static boolean isValidMaxUpdateAge(long v) { return v >= 0 || v == IMPLICIT_MAX_UPDATE_AGE; } Try / catch
try {
return builder.setMaxUpdateAgeMillis(age);
} catch (IllegalArgumentException e) {
if (!e.getMessage().contains("maxUpdateAgeMillis")) throw e;
return builder.setMaxUpdateAgeMillis(IMPLICIT_MAX_UPDATE_AGE); // sentinel fallback
} Prevention
- Always reference IMPLICIT_MAX_UPDATE_AGE instead of hard-coding -1 to mean 'default'.
- Treat -1 as a reserved sentinel: reject all other negatives in your own config layer.
- Document in your settings schema that maxUpdateAge accepts [0, +inf) or the sentinel.
When it happens
Trigger: Calling builder.setMaxUpdateAgeMillis(-1) expecting the default without using the IMPLICIT_MAX_UPDATE_AGE constant, or passing any negative long such as setMaxUpdateAgeMillis(-5000) from a computed or config-sourced value.
Common situations: Developers hard-coding -1 as a 'reset to default' value instead of referencing LocationRequest.Builder.IMPLICIT_MAX_UPDATE_AGE; parsing settings where -1 and other negatives were not distinguished; unit conversion introducing a negative sign.
Related errors
- intervalMillis must be greater than 0
- maxUpdateDelayMillis must be greater than or equal to 0
- deleteAll was set to true but keys were also provided
- Element in keys cannot be null or empty
- retrieveAll was set to true but other constraint(s) was also
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/4452196287918da0.
Report an issue: GitHub.