apache/beam · error
bad nullable: %+v
Error message
bad nullable: %+v
What it means
DecodeCoderRef converts an encoded CoderRef back into a coder. A nullable-type ref must have exactly one component (the inner coder); any other count indicates a malformed or incompatible encoded form, so decoding aborts with the offending ref printed.
Solutions
- Regenerate the pipeline artifact from the original pipeline; don't hand-edit encoded coder graphs.
- Match the decoding SDK version to the encoding one.
- Inspect the printed CoderRef to find which graph-rewriting step broke the component list.
- If constructing CoderRefs programmatically, always wrap exactly one component for nullableType.
Example fix
// before
ref := &graphx.CoderRef{Type: graphx.Nullable, Components: nil}
c, err := graphx.DecodeCoderRef(ref)
// after
ref := &graphx.CoderRef{Type: graphx.Nullable, Components: []*graphx.CoderRef{innerRef}}
c, err := graphx.DecodeCoderRef(ref) Defensive patterns
Strategy: try-catch
Validate before calling
func validNullableRef(r *graphx.CoderRef) bool {
return r != nil && r.Type == graphx.Nullable && len(r.Components) == 1
} Type guard
func isNullableRef(r *graphx.CoderRef) bool { return r != nil && r.Type == graphx.Nullable && len(r.Components) == 1 } Try / catch
c, err := graphx.DecodeCoderRef(ref)
if err != nil {
if strings.Contains(err.Error(), "bad nullable") {
return nil, fmt.Errorf("corrupt nullable coder ref %v: %w", ref, err)
}
return nil, err
} Prevention
- Regenerate pipeline artifacts rather than reusing stale ones after SDK upgrades
- Keep graph-rewriting passes component-count-preserving
- Round-trip test: encode then decode every custom coder graph in tests
When it happens
Trigger: Decoding a CoderRef with Type nullableType whose Components has length != 1 — during pipeline deserialization from a model pipeline (e.g. loading a saved pipeline or processing a Dataflow job description).
Common situations: Hand-edited or truncated pipeline JSON/proto artifacts; cross-SDK-version coder graphs; custom graph transforms that added or dropped components on nullable coders.
Related errors
- bad iterable/stream: %+v
- bad length prefix: %+v
- bad pair: %+v
- bad windowed value: %+v
- failed to marshal Nullable coder
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/b6efc2b7c7d81fcd.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/core/runtime/graphx/dataflow.go:287
return nil, err
}
t := typex.New(typex.CoGBKType, append([]typex.FullType{key.T}, coder.Types(values)...)...)
return &coder.Coder{Kind: coder.CoGBK, T: t, Components: append([]*coder.Coder{key}, values...)}, nil
}
}
value, err := DecodeCoderRef(elm)
if err != nil {
return nil, err
}
t := typex.New(typex.KVType, key.T, value.T)
return &coder.Coder{Kind: coder.KV, T: t, Components: []*coder.Coder{key, value}}, nil
case nullableType:
if len(c.Components) != 1 {
return nil, errors.Errorf("bad nullable: %+v", c)
}
inner, err := DecodeCoderRef(c.Components[0])
if err != nil {
return nil, err
}
t := typex.New(typex.NullableType, inner.T)
return &coder.Coder{Kind: coder.Nullable, T: t, Components: []*coder.Coder{inner}}, nil
case lengthPrefixType:
if len(c.Components) != 1 {
return nil, errors.Errorf("bad length prefix: %+v", c)
}
subC := c.Components[0]
switch subC.Type {
case stringType: // Needs special handling if wrapped by dataflow.View on GitHub (pinned to 12126d8942)