{"record":{"id":"41c18fbd8fa25c8c","repo":"apache/beam","slug":"projectid-can-t-be-empty","errorCode":null,"errorMessage":"projectId can't be empty","messagePattern":"projectId can't be empty","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/SpannerTransformRegistrar.java","lineNumber":125,"sourceCode":"    public void setExperimentalHost(@Nullable String experimentalHost) {\n      this.experimentalHost = experimentalHost;\n    }\n\n    public void setPlainText(@Nullable Boolean plainText) {\n      this.plainText = plainText;\n    }\n\n    public void setClientCertPath(@Nullable String clientCertPath) {\n      this.clientCertPath = clientCertPath;\n    }\n\n    public void setClientCertKeyPath(@Nullable String clientCertKeyPath) {\n      this.clientCertKeyPath = clientCertKeyPath;\n    }\n\n    void checkMandatoryFields() {\n      if (projectId.isEmpty()) {\n        throw new IllegalArgumentException(\"projectId can't be empty\");\n      }\n      if (databaseId.isEmpty()) {\n        throw new IllegalArgumentException(\"databaseId can't be empty\");\n      }\n      if (instanceId.isEmpty()) {\n        throw new IllegalArgumentException(\"instanceId can't be empty\");\n      }\n      if ((clientCertPath != null) != (clientCertKeyPath != null)) {\n        throw new IllegalArgumentException(\n            \"Both clientCertPath and clientCertKeyPath must be specified together.\");\n      }\n    }\n  }\n\n  public static class ReadBuilder\n      implements ExternalTransformBuilder<ReadBuilder.Configuration, PBegin, PCollection<Row>> {\n\n    public static class Configuration extends CrossLanguageConfiguration {","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/SpannerTransformRegistrar.java#L107-L143","documentation":"SpannerTransformRegistrar's Configuration (the cross-language/schematized transform registration for SpannerRead) validates mandatory fields via checkMandatoryFields(); an empty projectId throws IllegalArgumentException(\"projectId can't be empty\"). A Spanner connection is impossible without a project, so this fails fast at configuration time.","triggerScenarios":"Submitting a Spanner read/write transform (e.g., via cross-language expansion or SpannerTransformRegistrar.Configuration) where the projectId option was not set or was set to an empty string before checkMandatoryFields() runs.","commonSituations":"Omitting --projectId in pipeline options when constructing the transform from YAML/SQL/cross-language pipelines; env-provided GCP project not propagated into the configuration; typo between option name and expected key.","solutions":["Set projectId explicitly: SpannerConfig.create().withProjectId(\"my-project\") or pass the projectId parameter to the transform.","When using pipeline options, wire the GcpOptions.getProject() into the Spanner config instead of relying on defaults.","For cross-language/YAML usage, supply the projectId field in the transform's arguments.","Validate the config object (projectId, instanceId, databaseId all non-empty) before submitting the pipeline."],"exampleFix":"// before\nSpannerConfig config = SpannerConfig.create().withInstanceId(\"my-instance\").withDatabaseId(\"my-db\");\n// after\nSpannerConfig config = SpannerConfig.create()\n    .withProjectId(\"my-gcp-project\")\n    .withInstanceId(\"my-instance\")\n    .withDatabaseId(\"my-db\");","handlingStrategy":"validation","validationCode":"Preconditions.checkArgument(\n    config.getProjectId() != null && !config.getProjectId().isEmpty(), \"projectId must be set\");\nPreconditions.checkArgument(!config.getInstanceId().isEmpty(), \"instanceId must be set\");\nPreconditions.checkArgument(!config.getDatabaseId().isEmpty(), \"databaseId must be set\");","typeGuard":null,"tryCatchPattern":"try { pipeline.apply(SpannerRead.of(config).withTable(t)); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"projectId can't be empty\")) { config = config.withProjectId(gcpOptions.getProject()); } else throw e; }","preventionTips":["Always set withProjectId explicitly rather than relying on ambient defaults.","Wire GcpOptions project into SpannerConfig in pipeline option processing.","Validate all three of projectId/instanceId/databaseId before submitting."],"tags":["java","beam","spanner","config","validation"],"backgroundTag":"missing-required-config-field","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}