microg/GmsCore · error · IllegalStateException
DataSet has already been built.
Error message
DataSet has already been built.
What it means
Thrown by DataSet.Builder.add(DataPoint) after build() has already been called. The DataSet.Builder is single-use; once the DataSet is built, adding more points is rejected as an invalid state to prevent mutating an immutable, possibly already-consumed DataSet.
Source
Thrown at play-services-fitness/src/main/java/com/google/android/gms/fitness/data/DataSet.java:178
* Builder used to create new data sets.
*/
public static class Builder {
private final DataSet dataSet;
private boolean built = false;
Builder(DataSource dataSource) {
this.dataSet = DataSet.create(dataSource);
}
/**
* Adds a data point to this data set. The data points should be for the correct data type and data source, and should have its timestamp
* already set.
*
* @throws IllegalArgumentException If dataPoint has the wrong {@link DataSource}, or contain invalid data.
*/
@NonNull
public Builder add(@NonNull DataPoint dataPoint) {
if (built) throw new IllegalStateException("DataSet has already been built.");
this.dataSet.add(dataPoint);
return this;
}
/**
* Adds a list of data points to this data set in bulk. All data points should be for the correct data type and data source, and should have their
* timestamp already set.
*
* @throws IllegalArgumentException If the {@code dataPoints} have the wrong source, or contain invalid data.
*/
@NonNull
public Builder addAll(@NonNull Iterable<DataPoint> iterable) {
if (built) throw new IllegalStateException("DataSet has already been built.");
this.dataSet.addAll(iterable);
return this;
}
/**View on GitHub (pinned to 157c9d86ac)
Solutions
- Call add() for ALL data points before invoking build().
- Create a new DataSet.builder(dataSource) for each batch of points you want to commit separately.
- Restructure collection logic to accumulate points first, then build once at the end.
- Never cache or share a Builder instance after build() — treat it as consumed.
Example fix
// before DataSet.Builder b = DataSet.builder(ds); b.build(); b.add(laterPoint); // throws // after DataSet first = DataSet.builder(ds).add(p1).build(); DataSet second = DataSet.builder(ds).add(laterPoint).build();
Defensive patterns
Strategy: validation
Validate before calling
if (builderConsumed) { builder = DataSet.builder(dataSource); } Type guard
boolean canAdd(DataSet.Builder b) { return !b.isBuilt(); } // track in your own wrapper Try / catch
try { builder.add(point); } catch (IllegalStateException e) { builder = DataSet.builder(dataSource); builder.add(point); } Prevention
- Call build() only after all points are added.
- Create a fresh builder per batch/commit.
- Discard the Builder reference right after build().
- Avoid sharing builders across threads or callbacks.
When it happens
Trigger: Holding a DataSet.Builder reference, calling build(), then calling Builder.add(dataPoint) with a new point — often when streaming additional points after an initial build.
Common situations: Incremental data collection where code keeps appending to a builder after committing the DataSet; sharing a Builder across threads or callbacks where one path builds and another keeps adding.
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
- Only one extension per type may be added
- Cannot add twice the same OnSwitchChangeListener
- Cannot remove OnSwitchChangeListener
- The number of values does not match the number of fields
- DataPoint already built
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/9e3e557e8539bb98.
Report an issue: GitHub.