apache/beam · error
cannot make a state provider for an unkeyed input
Error message
cannot make a state provider for an unkeyed input %v
What it means
userStateAdapter.NewStateProvider creates a state provider bound to a keyed element. Keyed state requires a key coder (kc); when the input was registered without a key (unkeyed), no state can be scoped to an element key, so the call fails naming the element. It mirrors the keyed side-input check: state APIs are only valid for keyed PCollections.
Solutions
- Key the input PCollection before the stateful transform (emit KVs, e.g. via beam.ParDo mapping element -> KV(key, element)).
- Remove state usage from the DoFn if per-key state is not actually needed.
- Check the state spec's key coder is populated in the pipeline proto (stateIDToKeyCoder).
- Verify the DoFn's main input type is a KV matching the declared key coder.
Example fix
// before: stateful DoFn over unkeyed elements
beam.ParDo(s, statefulFn, unkeyedPCol)
// after: key elements first
keyed := beam.ParDo(s, func(v string) (string, string) { return keyFor(v), v }, unkeyedPCol)
beam.ParDo(s, statefulFn, keyed) Defensive patterns
Strategy: validation
Validate before calling
if adapter.Kc() == nil { return errors.New("state requires a keyed input; key the PCollection first") } Type guard
func supportsState(a *exec.UserStateAdapter) bool { return a != nil && a.HasKeyCoder() } Try / catch
sp, err := adapter.NewStateProvider(ctx, reader, w, element)
if err != nil && strings.Contains(err.Error(), "unkeyed input") {
return nil, fmt.Errorf("stateful DoFn used over unkeyed input: %w", err)
} Prevention
- Only attach state APIs to DoFns whose main input is a KV
- Key the PCollection before the stateful transform
- Verify key coders are populated in the bundle's state specs
When it happens
Trigger: Calling NewStateProvider on a userStateAdapter built with kc == nil — i.e. using stateful DoFn state APIs (bag, map, ordered list, combining state) against a main input that has no key.
Common situations: Adding state annotations (SetupProcessBundle state specs) to a DoFn whose main input is not a KV; building a pipeline in Go where the input PCollection was never keyed (missing ParDo/KV step); custom runners wiring state adapters for unkeyed inputs.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- cannot make a keyed iterable for an unkeyed side input
- invalid varint in ordered list entry
- mismatched Flatten input types
- unable to do row encoding
- unexpected error reading varint
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/5f03c29c54a8435f.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/core/runtime/exec/userstate.go:662
// NewUserStateAdapter returns a user state adapter for the given StreamID and coder.
// It expects a W<V> or W<KV<K,V>> coder, because the protocol requires windowing information.
func NewUserStateAdapter(sid StreamID, c *coder.Coder, stateIDToCoder map[string]*coder.Coder, stateIDToKeyCoder map[string]*coder.Coder, stateIDToCombineFn map[string]*graph.CombineFn) UserStateAdapter {
if !coder.IsW(c) {
panic(fmt.Sprintf("expected WV coder for user state %v: %v", sid, c))
}
wc := MakeWindowEncoder(c.Window)
var kc ElementEncoder
if coder.IsKV(coder.SkipW(c)) {
kc = MakeElementEncoder(coder.SkipW(c).Components[0])
}
return &userStateAdapter{sid: sid, wc: wc, kc: kc, c: c, stateIDToCoder: stateIDToCoder, stateIDToKeyCoder: stateIDToKeyCoder, stateIDToCombineFn: stateIDToCombineFn}
}
// NewStateProvider creates a stateProvider with the ability to talk to the state API.
func (s *userStateAdapter) NewStateProvider(ctx context.Context, reader StateReader, w typex.Window, element any) (stateProvider, error) {
if s.kc == nil {
return stateProvider{}, fmt.Errorf("cannot make a state provider for an unkeyed input %v", element)
}
elementKey, err := EncodeElement(s.kc, element.(*MainInput).Key.Elm)
if err != nil {
return stateProvider{}, err
}
win, err := EncodeWindow(s.wc, w)
if err != nil {
return stateProvider{}, err
}
sp := stateProvider{
ctx: ctx,
sr: reader,
SID: s.sid,
elementKey: elementKey,
window: win,
transactionsByKey: make(map[string][]state.Transaction),
initialValueByKey: make(map[string]any),View on GitHub (pinned to 12126d8942)