microg/GmsCore · error · IllegalStateException

Position already set using positionFromBounds()

Error message

Position already set using positionFromBounds()

What it means

GroundOverlayOptions.position(LatLng, float width) throws IllegalStateException when the position was already specified via positionFromBounds(LatLngBounds). A ground overlay supports exactly one positioning strategy — an anchor point with explicit dimensions, or bounds-fitting — and calling both is contradictory. The builder detects the conflict by checking the internal bounds field.

Source

Thrown at play-services-maps/src/main/java/com/google/android/gms/maps/model/GroundOverlayOptions.java:291

     *                 given image will remain fixed. The anchor will remain fixed to the position
     *                 on the ground when transformations are applied (e.g., setDimensions,
     *                 setBearing, etc.).
     * @param width    the width of the overlay (in meters). The height will be determined
     *                 automatically based on the image proportions.
     * @return this {@link GroundOverlayOptions} object with a new position set.
     * @throws IllegalArgumentException if anchor is null
     * @throws IllegalArgumentException if width is negative
     * @throws IllegalStateException    if the position was already set using
     *                                  {@link #positionFromBounds(LatLngBounds)}
     */
    public GroundOverlayOptions position(LatLng location, float width)
            throws IllegalArgumentException, IllegalStateException {
        if (location == null)
            throw new IllegalArgumentException("location must not be null");
        if (width < 0)
            throw new IllegalArgumentException("width must not be negative");
        if (bounds != null)
            throw new IllegalStateException("Position already set using positionFromBounds()");
        this.location = location;
        this.width = width;
        return this;
    }

    /**
     * Specifies the position for this ground overlay. When rendered, the image will be scaled to
     * fit the bounds (i.e., its proportions will not necessarily be preserved).
     *
     * @param bounds a {@link LatLngBounds} in which to place the ground overlay
     * @return this {@link GroundOverlayOptions} object with a new position set.
     * @throws IllegalStateException if the position was already set using
     *                               {@link #position(LatLng, float)} or
     *                               {@link #position(LatLng, float, float)}
     */
    public GroundOverlayOptions positionFromBounds(LatLngBounds bounds)
            throws IllegalStateException {
        if (location != null)

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Pick one positioning method: remove the position(...) call if using positionFromBounds(...), or the reverse.
  2. When reusing options across overlays, create a fresh GroundOverlayOptions per overlay instead of mutating a shared builder.
  3. Track which positioning method was applied (a flag or separate builder paths) and call only one.

Example fix

// before
GroundOverlayOptions opts = new GroundOverlayOptions()
    .positionFromBounds(bounds);
if (anchor != null) {
    opts.position(anchor, 1000f); // IllegalStateException
}

// after
GroundOverlayOptions opts = new GroundOverlayOptions();
if (anchor != null) {
    opts.position(anchor, 1000f);
} else {
    opts.positionFromBounds(bounds);
}
Defensive patterns

Strategy: validation

Validate before calling

if (usingBounds) { /* do not call position(...) */ }

Type guard

boolean isPositionFree(GroundOverlayOptions o) { return !o.getBoundsSet(); } // track it yourself with a flag

Try / catch

try {
    options.position(anchor, width);
} catch (IllegalStateException e) {
    // already positioned via positionFromBounds(); keep that strategy
}

Prevention

When it happens

Trigger: Fluent chains that call positionFromBounds(...) earlier and then position(...) later (or vice versa) on the same GroundOverlayOptions, often when options are assembled conditionally from different code paths that each set a position.

Common situations: Reusing a single GroundOverlayOptions instance across multiple overlays where one path sets bounds and another sets position; refactoring code that switched positioning strategy without removing the old call; conditional configuration where both branches execute.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/70197f87caa5e9c9. Report an issue: GitHub.