apache/beam · error · IllegalArgumentException

MetadataInstance can't be empty

Error message

MetadataInstance can't be empty

What it means

Thrown by ChangeStreamReaderBuilder.build() when building a Spanner change-stream read: the configured metadataInstance is an empty string. The metadata instance is the Cloud Spanner instance where the connector stores change-stream connector metadata (heartbeats, partitions state). The library refuses to construct the transform without it because writes would fail downstream.

Source

Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/SpannerTransformRegistrar.java:503

        if (watermarkRefreshRateString != null) {
          this.watermarkRefreshRate = Duration.parse(watermarkRefreshRateString);
        }
      }
    }

    @Override
    @NonNull
    public PTransform<PBegin, PCollection<String>> buildExternal(
        ChangeStreamReaderBuilder.Configuration configuration) {

      configuration.checkMandatoryFields();

      if (configuration.changeStreamName.isEmpty()) {
        throw new IllegalArgumentException("ChangeStreamName can't be empty");
      }

      if (configuration.metadataInstance.isEmpty()) {
        throw new IllegalArgumentException("MetadataInstance can't be empty");
      }

      if (configuration.metadataDatabase.isEmpty()) {
        throw new IllegalArgumentException("MetadataDatabase can't be empty");
      }

      SpannerIO.ReadChangeStream readChangeStream =
          SpannerIO.readChangeStream()
              .withProjectId(configuration.projectId)
              .withInstanceId(configuration.instanceId)
              .withDatabaseId(configuration.databaseId)
              .withChangeStreamName(configuration.changeStreamName)
              .withMetadataInstance(configuration.metadataInstance)
              .withMetadataDatabase(configuration.metadataDatabase);

      if (configuration.inclusiveStartAt != null) {
        readChangeStream = readChangeStream.withInclusiveStartAt(configuration.inclusiveStartAt);
      }

View on GitHub (pinned to 12126d8942)

Solutions

  1. Set withMetadataInstance("your-instance") (or fill the metadataInstance field in your pipeline options) with a valid Cloud Spanner instance ID.
  2. If metadata should live in the same instance as the change stream, copy the instanceId value into metadataInstance.
  3. Validate configuration before submitting the pipeline so the failure surfaces at config time rather than at graph construction.

Example fix

// before
SpannerIO.readChangeStream()
    .withProjectId(project)
    .withInstanceId(instance)
    .withMetadataInstance("")
// after
SpannerIO.readChangeStream()
    .withProjectId(project)
    .withInstanceId(instance)
    .withMetadataInstance(instance)
Defensive patterns

Strategy: validation

Validate before calling

// before building
if (metadataInstance == null || metadataInstance.isEmpty()) {
  throw new IllegalArgumentException("metadataInstance must be set to a non-empty Spanner instance ID");
}

Prevention

When it happens

Trigger: Calling SpannerIO.readChangeStream() (or a ChangeStreamReaderBuilder via the transform registrar) and setting withMetadataInstance("") or never calling withMetadataInstance at all while the surrounding config leaves the field empty, then invoking build().

Common situations: YAML/JSON pipeline options where the metadataInstance key is present but empty; templated pipelines with a blank parameter; copying a pipeline config that had instanceId but forgot the separate metadataInstance field.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/80222dcab0f6abaa. Report an issue: GitHub.