apache/beam · error

NewSK: keyCoder must not be nil

Error message

NewSK: keyCoder must not be nil

What it means

NewSK in sdks/go/pkg/beam/core/graph/coder/coder.go panics with "NewSK: keyCoder must not be nil" when constructing a ShardedKey coder without a key coder. A ShardedKey coder is a composite whose single component is the key's coder, mirroring KV, so nil is invalid. The panic is a fail-fast constructor contract check.

Solutions

  1. Build the key coder before NewSK: kc := coder.NewJ(keyType); skc := coder.NewSK(kc).
  2. Fix the code producing the nil key coder — check every (coder, err) return for swallowed errors.
  3. Add a nil assertion before the call to surface the problem with more context.

Example fix

// before
skc := coder.NewSK(nil) // panics
// after
kc := coder.NewJ(reflect.TypeOf(""))
skc := coder.NewSK(kc)
Defensive patterns

Strategy: validation

Validate before calling

if kc == nil {
    return nil, errors.New("key coder must be initialized before NewSK")
}
skc := coder.NewSK(kc)

Type guard

func isCoderReady(c *coder.Coder) bool { return c != nil && c.T != nil }

Try / catch

func newSKSafe(kc *coder.Coder) (skc *coder.Coder, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("NewSK failed: %v", r)
        }
    }()
    return coder.NewSK(kc), nil
}

Prevention

When it happens

Trigger: Calling coder.NewSK(nil) directly or via makeCoder in tests, or passing a key coder from a failed lookup that returned nil with its error ignored.

Common situations: Pipelines using sharded keys (TryCombinePerKey outputs, TestShardedKeyCoder_WireFormat) where key-coder wiring was assembled dynamically and lost initialization.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/core/graph/coder/coder.go:533

func NewCoGBK(components []*Coder) *Coder {
	checkCodersNotNil(components)
	return &Coder{
		Kind:       CoGBK,
		T:          typex.New(typex.CoGBKType, Types(components)...),
		Components: components,
	}
}

// NewSK returns a coder for ShardedKey-typed values. The component
// keyCoder encodes the user key; the ShardID is encoded as a
// length-prefixed byte string preceding it (beam:coder:sharded_key:v1).
//
// The resulting FullType root is typex.ShardedKeyType with the key's
// FullType as the single component, following the same Composite
// pattern as KV.
func NewSK(keyCoder *Coder) *Coder {
	if keyCoder == nil {
		panic("NewSK: keyCoder must not be nil")
	}
	return &Coder{
		Kind:       ShardedKey,
		T:          typex.New(typex.ShardedKeyType, keyCoder.T),
		Components: []*Coder{keyCoder},
	}
}

// IsSK returns true iff the coder is for a ShardedKey.
func IsSK(c *Coder) bool {
	return c != nil && c.Kind == ShardedKey
}

// SkipW returns the data coder used by a WindowedValue, or returns the coder. This
// allows code to seamlessly traverse WindowedValues without additional conditional
// code.
func SkipW(c *Coder) *Coder {
	if c.Kind == WindowedValue {

View on GitHub (pinned to 12126d8942)