{"record":{"id":"54fa4dbff87b4530","repo":"apache/beam","slug":"unexpected-null-pipeline-option-for-dynamicdestination","errorCode":null,"errorMessage":"Unexpected null pipeline option for DynamicDestination object. Need to call setSideInputAccessorFromProcessContext(context) before use it.","messagePattern":"Unexpected null pipeline option for DynamicDestination object\\. Need to call setSideInputAccessorFromProcessContext\\(context\\) before use it\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/UpdateSchemaDestination.java","lineNumber":121,"sourceCode":"    this.zeroLoadJobIdPrefixView = zeroLoadJobIdPrefixView;\n    this.bqServices = bqServices;\n    this.maxRetryJobs = maxRetryJobs;\n    this.kmsKey = kmsKey;\n    this.schemaUpdateOptions = schemaUpdateOptions;\n    this.createDisposition = createDisposition;\n    this.writeDisposition = writeDisposition;\n    this.dynamicDestinations = dynamicDestinations;\n  }\n\n  @StartBundle\n  public void startBundle(StartBundleContext c) {\n    pendingJobs.clear();\n  }\n\n  TableDestination getTableWithDefaultProject(DestinationT destination) {\n    PipelineOptions pipelineOptions = dynamicDestinations.getPipelineOptions();\n    if (pipelineOptions == null) {\n      throw new IllegalStateException(\n          \"Unexpected null pipeline option for DynamicDestination object. \"\n              + \"Need to call setSideInputAccessorFromProcessContext(context) before use it.\");\n    }\n    BigQueryOptions options = pipelineOptions.as(BigQueryOptions.class);\n    TableDestination tableDestination = dynamicDestinations.getTable(destination);\n    TableReference tableReference = tableDestination.getTableReference();\n\n    if (Strings.isNullOrEmpty(tableReference.getProjectId())) {\n      tableReference.setProjectId(\n          options.getBigQueryProject() == null\n              ? options.getProject()\n              : options.getBigQueryProject());\n      tableDestination = tableDestination.withTableReference(tableReference);\n    }\n\n    return tableDestination;\n  }\n","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/UpdateSchemaDestination.java#L103-L139","documentation":"UpdateSchemaDestination.getTableWithDefaultProject resolves the destination table via DynamicDestinations, which needs PipelineOptions that are injected by calling setSideInputAccessorFromProcessContext(processContext). If getPipelineOptions() returns null, the object was used before that call, so it throws IllegalStateException instructing the developer to inject the context first.","triggerScenarios":"dynamicDestinations.getTable (via tableDestination) is invoked before setSideInputAccessorFromProcessContext(context) was called — e.g. in setup, startBundle, or a custom path that bypasses processElement initialization.","commonSituations":"Calling getTable/getDestination in setup() instead of processElement, subclassing DynamicDestinations and using table resolution too early, or reusing an UpdateSchemaDestination across bundles without re-binding the context.","solutions":["Call dynamicDestinations.setSideInputAccessorFromProcessContext(context) before any getTable/getDestination usage, typically at the start of processElement","Move destination resolution out of setup()/constructor into processElement after context binding","If implementing a custom path, mimic BigQueryIO's WriteTables/CreateTables flow which performs the binding first","Verify the DynamicDestinations instance isn't shared across DoFn instances in a way that loses the binding","Guard with a null check and fail fast with a clear message in your own wrapper"],"exampleFix":"// before (in setup)\nTableDestination dest = updateSchemaDestination.getTableWithDefaultProject(destination);\n// after (in processElement)\npublic void processElement(ProcessContext context) {\n  updateSchemaDestination.setSideInputAccessorFromProcessContext(context);\n  TableDestination dest = updateSchemaDestination.getTableWithDefaultProject(destination);\n}","handlingStrategy":"type-guard","validationCode":"if (dynamicDestinations.getPipelineOptions() == null) {\n  dynamicDestinations.setSideInputAccessorFromProcessContext(context);\n}","typeGuard":"boolean optionsBound(DynamicDestinations<?> dd) {\n  return dd.getPipelineOptions() != null;\n}","tryCatchPattern":"try {\n  TableDestination dest = updateSchemaDestination.getTableWithDefaultProject(destination);\n} catch (IllegalStateException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"setSideInputAccessorFromProcessContext\")) {\n    updateSchemaDestination.setSideInputAccessorFromProcessContext(context);\n    // retry once\n  } else throw e;\n}","preventionTips":["Always call setSideInputAccessorFromProcessContext before resolving destinations","Resolve destinations inside processElement, not setup()/constructor","Follow BigQueryIO's CreateTables/WriteTables pattern when subclassing","Don't share DynamicDestinations across DoFn instances"],"tags":["java","google-bigquery","dynamicdestinations","lifecycle"],"backgroundTag":"null-argument","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}