{"record":{"id":"70197f87caa5e9c9","repo":"microg/GmsCore","slug":"position-already-set-using-positionfrombounds","errorCode":null,"errorMessage":"Position already set using positionFromBounds()","messagePattern":"Position already set using positionFromBounds\\(\\)","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"play-services-maps/src/main/java/com/google/android/gms/maps/model/GroundOverlayOptions.java","lineNumber":291,"sourceCode":"     *                 given image will remain fixed. The anchor will remain fixed to the position\n     *                 on the ground when transformations are applied (e.g., setDimensions,\n     *                 setBearing, etc.).\n     * @param width    the width of the overlay (in meters). The height will be determined\n     *                 automatically based on the image proportions.\n     * @return this {@link GroundOverlayOptions} object with a new position set.\n     * @throws IllegalArgumentException if anchor is null\n     * @throws IllegalArgumentException if width is negative\n     * @throws IllegalStateException    if the position was already set using\n     *                                  {@link #positionFromBounds(LatLngBounds)}\n     */\n    public GroundOverlayOptions position(LatLng location, float width)\n            throws IllegalArgumentException, IllegalStateException {\n        if (location == null)\n            throw new IllegalArgumentException(\"location must not be null\");\n        if (width < 0)\n            throw new IllegalArgumentException(\"width must not be negative\");\n        if (bounds != null)\n            throw new IllegalStateException(\"Position already set using positionFromBounds()\");\n        this.location = location;\n        this.width = width;\n        return this;\n    }\n\n    /**\n     * Specifies the position for this ground overlay. When rendered, the image will be scaled to\n     * fit the bounds (i.e., its proportions will not necessarily be preserved).\n     *\n     * @param bounds a {@link LatLngBounds} in which to place the ground overlay\n     * @return this {@link GroundOverlayOptions} object with a new position set.\n     * @throws IllegalStateException if the position was already set using\n     *                               {@link #position(LatLng, float)} or\n     *                               {@link #position(LatLng, float, float)}\n     */\n    public GroundOverlayOptions positionFromBounds(LatLngBounds bounds)\n            throws IllegalStateException {\n        if (location != null)","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-maps/src/main/java/com/google/android/gms/maps/model/GroundOverlayOptions.java#L273-L309","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pick one positioning method: remove the position(...) call if using positionFromBounds(...), or the reverse.","When reusing options across overlays, create a fresh GroundOverlayOptions per overlay instead of mutating a shared builder.","Track which positioning method was applied (a flag or separate builder paths) and call only one."],"exampleFix":"// before\nGroundOverlayOptions opts = new GroundOverlayOptions()\n    .positionFromBounds(bounds);\nif (anchor != null) {\n    opts.position(anchor, 1000f); // IllegalStateException\n}\n\n// after\nGroundOverlayOptions opts = new GroundOverlayOptions();\nif (anchor != null) {\n    opts.position(anchor, 1000f);\n} else {\n    opts.positionFromBounds(bounds);\n}","handlingStrategy":"validation","validationCode":"if (usingBounds) { /* do not call position(...) */ }","typeGuard":"boolean isPositionFree(GroundOverlayOptions o) { return !o.getBoundsSet(); } // track it yourself with a flag","tryCatchPattern":"try {\n    options.position(anchor, width);\n} catch (IllegalStateException e) {\n    // already positioned via positionFromBounds(); keep that strategy\n}","preventionTips":["Choose one positioning strategy per overlay and enforce it in code structure.","Create a fresh GroundOverlayOptions per overlay instead of reusing a shared builder.","Build options in exclusive if/else branches."],"tags":["maps","android","ground-overlay","builder","state"],"backgroundTag":"invalid-state-transition","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}