microg/GmsCore · error · IllegalStateException

DataPoint already built

Error message

DataPoint already built

What it means

DataPoint.Builder.build() guards against reusing the same Builder after the DataPoint has already been produced. Once build() succeeds, the Builder's built flag is set and any further call to build() throws this IllegalStateException. Builders are single-use by design so the returned DataPoint cannot be mutated unexpectedly afterward.

Source

Thrown at play-services-fitness/src/main/java/com/google/android/gms/fitness/data/DataPoint.java:261

    }

    /**
     * Builder for {@link DataPoint} instances.
     */
    public static class Builder {
        private final DataPoint dataPoint;
        private boolean built = false;

        Builder(DataSource dataSource) {
            this.dataPoint = DataPoint.create(dataSource);
        }

        /**
         * Builds and returns the {@link DataPoint}.
         */
        @NonNull
        public DataPoint build() {
            if (built) throw new IllegalStateException("DataPoint already built");
            this.built = true;
            return this.dataPoint;
        }

        /**
         * Sets the value of an activity field to {@code activity}.
         *
         * @throws IllegalArgumentException If the given index is out of the range for this data type.
         * @throws IllegalStateException    If the field isn't of format {@link com.google.android.gms.fitness.data.Field#FORMAT_INT32}.
         */
        @NonNull
        public Builder setActivityField(@NonNull com.google.android.gms.fitness.data.Field field, @NonNull String activity) {
            if (built) throw new IllegalStateException("DataPoint already built");
            this.dataPoint.getValue(field).setActivity(activity);
            return this;
        }

        /**

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Create a new DataPoint.Builder for each DataPoint you need instead of reusing one Builder.
  2. Call build() exactly once, at the end of configuration, and store the returned DataPoint.
  3. If you need many similar points, wrap construction in a helper method that builds a fresh Builder per call.

Example fix

// before
DataPoint.Builder b = DataPoint.builder(ds).setIntValues(1);
points.add(b.build());
points.add(b.build()); // IllegalStateException
// after
for (int v : vals) {
    points.add(DataPoint.builder(ds).setIntValues(v).build());
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (builder == null || alreadyBuilt) {
    builder = DataPoint.builder(dataSource); // fresh Builder instead of reuse
}

Type guard

boolean isUsable(DataPoint.Builder b, Set<DataPoint.Builder> used) { return !used.contains(b); }

Try / catch

try {
    return builder.build();
} catch (IllegalStateException e) {
    // Builder already consumed: create a new Builder and configure it again
    builder = DataPoint.builder(dataSource);
    return builder.setFloatValues(values).build();
}

Prevention

When it happens

Trigger: Calling build() twice on the same DataPoint.Builder instance, e.g. inside a loop where the Builder is hoisted outside the loop, or in code paths where build() can be reached twice (retry logic, conditional branches).

Common situations: A Builder stored in a field or closure and reused to emit multiple data points for a DataSet; accidentally calling build() both to validate and to produce the final object; helper methods that take a Builder and call build() at the end while the caller also calls build().

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/bd369515f61b5585. Report an issue: GitHub.