apache/beam · error · IllegalArgumentException
Unexpected StateSpec
Error message
Unexpected StateSpec {} What it means
convertToBagSpecInternal converts a combining StateSpec into an equivalent bag StateSpec. It only recognizes the concrete internal spec types Beam itself creates (CombiningStateSpec / CombiningWithContextStateSpec); any other StateSpec implementation reaches the else branch and throws IllegalArgumentException. This is an internal-use invariant check protecting the conversion API.
Solutions
- Only create state specs via the StateSpecs factory methods; never pass custom StateSpec implementations.
- If a custom spec is legitimately needed, extend the conversion to handle it (upstream change) or subclass one of the recognized spec classes.
- Update/align the Beam SDK and runner versions so their internal StateSpec classes match.
Example fix
// before StateSpec<Object> custom = new MyCustomSpec<>(); // after StateSpec<CombiningState<...>> spec = StateSpecs.combining(coder, combineFn);
Defensive patterns
Strategy: validation
Validate before calling
// Verify the spec is one of Beam's own factory-produced specs before conversion
if (!(spec instanceof StateSpecs.CombiningStateSpec
|| spec instanceof StateSpecs.CombiningWithContextStateSpec)) {
throw new IllegalArgumentException("spec must come from StateSpecs.combining(...)");
} Try / catch
try {
bagSpec = convertToBagSpecInternal(spec);
} catch (IllegalArgumentException e) {
// use the original spec instead of converting
} Prevention
- Only build state specs with StateSpecs factory methods
- Do not subclass StateSpec for custom behavior; use supported extension points
- Keep Beam SDK and runner versions in sync
When it happens
Trigger: Passing a custom or foreign StateSpec implementation (not one of the specs produced by StateSpecs factory methods) into the bag-spec conversion path used internally when a CombiningState needs to be represented as a bag, e.g. during pipeline translation for partially-consumed combining state.
Common situations: Custom StateSpec subclasses or specs from third-party extensions flowing into internal conversion; version skew where a runner/plugin passes a spec class unknown to this code; misuse of internal-use-only APIs.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- cannot call getPipelineOptions() in a window-only context
- cannot call sideInput() in a window-only context
- Cannot create from non-Java
- Cannot read state-written iterable without state reader.
- error writing state
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/4e28f440ee0e6065.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/state/StateSpecs.java:357
* <p>Convert a combining state spec to a bag of accumulators.
*/
@Internal
public static <InputT, AccumT, OutputT> StateSpec<BagState<AccumT>> convertToBagSpecInternal(
StateSpec<CombiningState<InputT, AccumT, OutputT>> combiningSpec) {
if (combiningSpec instanceof CombiningStateSpec) {
// Checked above; conversion to a bag spec depends on the provided spec being one of those
// created via the factory methods in this class.
@SuppressWarnings("unchecked")
CombiningStateSpec<InputT, AccumT, OutputT> typedSpec =
(CombiningStateSpec<InputT, AccumT, OutputT>) combiningSpec;
return typedSpec.asBagSpec();
} else if (combiningSpec instanceof CombiningWithContextStateSpec) {
@SuppressWarnings("unchecked")
CombiningWithContextStateSpec<InputT, AccumT, OutputT> typedSpec =
(CombiningWithContextStateSpec<InputT, AccumT, OutputT>) combiningSpec;
return typedSpec.asBagSpec();
} else {
throw new IllegalArgumentException("Unexpected StateSpec " + combiningSpec);
}
}
/**
* <b><i>For internal use only; no backwards-compatibility guarantees.</i></b>
*
* <p>Convert a set state spec to a map-state spec.
*/
@Internal
public static <KeyT> StateSpec<MapState<KeyT, Boolean>> convertToMapSpecInternal(
StateSpec<SetState<KeyT>> setStateSpec) {
if (setStateSpec instanceof SetStateSpec) {
// Checked above; conversion to a map spec depends on the provided spec being one of those
// created via the factory methods in this class.
@SuppressWarnings("unchecked")
SetStateSpec<KeyT> typedSpec = (SetStateSpec<KeyT>) setStateSpec;
return typedSpec.asMapSpec();
} else {View on GitHub (pinned to 12126d8942)