apache/beam · error · IllegalArgumentException

Experimental host can only be used with instance id: +…

Error message

Experimental host can only be used with instance id: + EXPERIMENTAL_HOST_INSTANCE_ID

What it means

SpannerAccessor.buildSpannerOptions() allows an experimental Spanner host endpoint only as a special test/experiment path: when the experimentalHost option is set, the instance id must equal EXPERIMENTAL_HOST_INSTANCE_ID; otherwise it throws IllegalArgumentException. This guards the experimental feature so it cannot be accidentally combined with production instance configurations.

Solutions

  1. Remove the experimentalHost option to use the standard Spanner endpoint
  2. Set the instance id exactly to EXPERIMENTAL_HOST_INSTANCE_ID if you truly intend to use the experimental host
  3. Check pipeline options/argv for stray --experimentalHost flags
  4. Verify which SpannerAccessor/SpannerConfig instance is being built and its instanceId value

Example fix

// before
--experimentalHost=staging.googleapis.com --spannerInstanceId=prod-instance
// after
--spannerInstanceId=prod-instance  // experimentalHost removed
Defensive patterns

Strategy: validation

Validate before calling

if (experimentalHost != null && !experimentalHost.isEmpty()
    && !EXPERIMENTAL_HOST_INSTANCE_ID.equals(instanceId)) {
  throw new IllegalArgumentException("experimentalHost requires instance id " + EXPERIMENTAL_HOST_INSTANCE_ID);
}

Try / catch

try { accessor = SpannerAccessor.getOrCreate(config); } catch (IllegalArgumentException e) { /* strip experimentalHost and retry */ }

Prevention

When it happens

Trigger: Setting SpannerConfig.experimentalHost (e.g. via --experimentalHost or withExperimentalHost) to a non-empty value while spannerConfig.instanceId is missing or does not equal EXPERIMENTAL_HOST_INSTANCE_ID.

Common situations: Copy-pasting experimental host flags from internal docs/testing into production pipeline options; leaving an experimental host env/option set in CI; mixing standard instance configs with an experimental endpoint.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

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

    ValueProvider<String> projectId = spannerConfig.getProjectId();
    if (projectId != null) {
      builder.setProjectId(projectId.get());
    }
    ServiceFactory<Spanner, SpannerOptions> serviceFactory = spannerConfig.getServiceFactory();
    if (serviceFactory != null) {
      builder.setServiceFactory(serviceFactory);
    }
    builder.setHost(spannerConfig.getHostValue());

    ValueProvider<String> experimentalHost = spannerConfig.getExperimentalHost();
    if (experimentalHost != null && !Strings.isNullOrEmpty(experimentalHost.get())) {
      builder.setExperimentalHost(experimentalHost.get());
      ValueProvider<Boolean> plainText = spannerConfig.getPlainText();
      ValueProvider<String> instanceId = spannerConfig.getInstanceId();
      if (Strings.isNullOrEmpty(instanceId.get())
          || !instanceId.get().equals(EXPERIMENTAL_HOST_INSTANCE_ID)) {
        throw new IllegalArgumentException(
            "Experimental host can only be used with instance id: "
                + EXPERIMENTAL_HOST_INSTANCE_ID);
      }
      if (plainText != null && Boolean.TRUE.equals(plainText.get())) {
        builder.setChannelConfigurator(b -> b.usePlaintext());
      }
      ValueProvider<String> clientCert = spannerConfig.getClientCertPath();
      ValueProvider<String> clientKey = spannerConfig.getClientCertKeyPath();
      if (clientCert != null
          && clientKey != null
          && clientCert.isAccessible()
          && clientKey.isAccessible()
          && !Strings.isNullOrEmpty(clientCert.get())
          && !Strings.isNullOrEmpty(clientKey.get())) {
        builder.useClientCert(clientCert.get(), clientKey.get());
      }
    }

View on GitHub (pinned to 12126d8942)