apache/beam · error

expected WV coder for user state

Error message

expected WV coder for user state %v: %v

What it means

NewUserStateAdapter builds a state adapter for Beam Go user state. The runner protocol requires state coders to carry windowing information, so the coder must be a W<V> or W<KV<K,V>> wrapper. If coder.IsW(c) is false, the library panics because state without window encoding cannot round-trip correctly.

Solutions

  1. Wrap the coder with coder.NewW(valueCoder) before passing it to NewUserStateAdapter
  2. Verify with coder.IsW(c) and inspect coder.SkipW(c) to confirm the inner coder is V or KV<K,V>
  3. Ensure the pipeline graph is built via the standard graphx/translate path, which applies window wrapping automatically

Example fix

// before
exec.NewUserStateAdapter(sid, kvCoder, ...)
// after
exec.NewUserStateAdapter(sid, coder.NewW(kvCoder), ...)
Defensive patterns

Strategy: validation

Validate before calling

if !coder.IsW(c) { return fmt.Errorf("state coder must be windowed: %v", c) }
exec.NewUserStateAdapter(sid, c, ...)

Type guard

func isWindowedCoder(c *coder.Coder) bool { return coder.IsW(c) }

Try / catch

defer func() { if r := recover(); r != nil { err = fmt.Errorf("user state adapter: %v", r) } }()

Prevention

When it happens

Trigger: Calling exec.NewUserStateAdapter with a coder that is not a windowed coder (e.g. a plain V or KV<K,V> coder) instead of coder.NewW(...) of the value coder.

Common situations: Constructing state adapters manually in custom runners or tests; passing a coder produced by graphx without wrapping; migrating pipelines between SDK versions where state coder wrapping conventions changed.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/7f204a52925f2a4c. Report an issue: GitHub.

Appendix: source

Thrown at sdks/go/pkg/beam/core/runtime/exec/userstate.go:648

type UserStateAdapter interface {
	NewStateProvider(ctx context.Context, reader StateReader, w typex.Window, element any) (stateProvider, error)
}

type userStateAdapter struct {
	sid                StreamID
	wc                 WindowEncoder
	kc                 ElementEncoder
	stateIDToCoder     map[string]*coder.Coder
	stateIDToKeyCoder  map[string]*coder.Coder
	stateIDToCombineFn map[string]*graph.CombineFn
	c                  *coder.Coder
}

// 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

View on GitHub (pinned to 12126d8942)