apache/beam · critical · IllegalStateException
sideInputAccessor (transient field) is null
Error message
sideInputAccessor (transient field) is null
What it means
DynamicDestinations.sideInput accesses a side input view, but the transient sideInputAccessor field is only wired up when a DoFn ProcessContext is available (setSideInputAccessorFromProcessContext). If sideInput() is called before the accessor is set — typically at pipeline-construction time or outside a DoFn lifecycle — it throws IllegalStateException.
Solutions
- Only call sideInput() from methods invoked during bundle processing (e.g. inside getDestination called at runtime via DynamicDestinations.Transforms), not from code running at construction time
- Ensure setSideInputAccessorFromProcessContext is invoked with the current ProcessContext before use
- Move side-input-dependent logic into the destination/table callbacks that execute per element
- In tests, set the accessor manually with a mock ProcessContext
Example fix
// before: side input read during pipeline construction
@Override
public TableDestination getDestination(ValueInSingleWindow<Element> element) {
String region = getSideInput(regionView).get(...); // accessor null at construction
...
}
// after: read from element/window context at process time
@Override
public TableDestination getDestination(ValueInSingleWindow<Element> element) {
String region = element.getOperand() != null
? computeRegion(element.getOperand())
: getSideInput(regionView).get(element.getWindow());
...
} Defensive patterns
Strategy: validation
Validate before calling
if (dynamicDestinations.getPipelineOptions() == null && !isInBundleProcessingContext()) {
throw new IllegalStateException("sideInput() only callable during bundle processing");
} Type guard
boolean canUseSideInputs(DynamicDestinations<?, ?> dd) { return dd.getSideInputs() != null && dd.isSideInputAccessorAvailable(); } Try / catch
try { region = getSideInput(view).get(window); } catch (IllegalStateException e) { LOG.error("Side input accessed at wrong lifecycle stage"); throw e; } Prevention
- Never call sideInput() from getDestinationCoder(), getSchema(), or other construction-time methods
- Read side inputs only in per-element/window methods invoked during bundle execution
- Set the accessor via setSideInputAccessorFromProcessContext before any access in custom setups
- Write lifecycle tests exercising getDestination at process time
When it happens
Trigger: Calling getSideInput/view inside getDestination(), getTable(), getFormatFunction(), or getDestinationCoder() that run during pipeline construction or in a non-DoFn context; calling sideInput from a worker thread outside the ProcessContext lifecycle; calling before startBundle.
Common situations: Users attempting to resolve per-destination side-input values in DynamicDestinations.getDestination at graph-building time; calling sideInput in tests without a DoFn.ProcessContext; serializing/deserializing the DynamicDestinations where the transient field is lost.
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
- Attempting to emit an element outside of a @ProcessElement…
- BigQuery storage source must be split before reading
- Cannot be called outside of a DoFn's process method.
- Cannot call refreshSchema after the object has been stopped!
- createRowWriter called when dynamicDestinations is null…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/cd2c3859b3242bae.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/DynamicDestinations.java:121
/**
* Specifies that this object needs access to one or more side inputs. This side inputs must be
* globally windowed, as they will be accessed from the global window.
*/
public List<PCollectionView<?>> getSideInputs() {
return Lists.newArrayList();
}
/**
* Returns the value of a given side input. The view must be present in {@link #getSideInputs()}.
*/
protected final <SideInputT> SideInputT sideInput(PCollectionView<SideInputT> view) {
checkState(
getSideInputs().contains(view),
"View %s not declared in getSideInputs() (%s)",
view,
getSideInputs());
if (sideInputAccessor == null) {
throw new IllegalStateException("sideInputAccessor (transient field) is null");
}
return sideInputAccessor.sideInput(view);
}
void setSideInputAccessorFromProcessContext(DoFn<?, ?>.ProcessContext context) {
this.sideInputAccessor = new SideInputAccessorViaProcessContext(context);
this.options = context.getPipelineOptions();
}
/**
* Returns an object that represents at a high level which table is being written to. May not
* return null.
*
* <p>The method must return a unique object for different destination tables involved over all
* BigQueryIO write transforms in the same pipeline. See
* https://github.com/apache/beam/issues/32335 for details.
*/
public abstract DestinationT getDestination(@Nullable ValueInSingleWindow<T> element);View on GitHub (pinned to 12126d8942)