apache/beam · error
bad decoding function
Error message
bad decoding function
What it means
encodeCustomCoder calls encodeUserFn on the decoding function c.Dec; if that function cannot be resolved to a serializable named function reference, the error is wrapped as "bad decoding function". Like its encoding counterpart, Beam can only serialize coders whose functions are named, top-level, addressable functions.
Source
Thrown at sdks/go/pkg/beam/core/runtime/graphx/serialize.go:149
outbound = append(outbound, &graph.Outbound{Type: t})
}
return opcode, u, wfn, inbound, outbound, nil
}
func encodeCustomCoder(c *coder.CustomCoder) (*v1pb.CustomCoder, error) {
t, err := encodeType(c.Type)
if err != nil {
return nil, errors.WithContextf(err, "encoding custom coder %v for type %v", c, c.Type)
}
enc, err := encodeUserFn(c.Enc)
if err != nil {
wrapped := errors.Wrap(err, "bad encoding function")
return nil, errors.WithContextf(wrapped, "encoding custom coder %v", c)
}
dec, err := encodeUserFn(c.Dec)
if err != nil {
wrapped := errors.Wrap(err, "bad decoding function")
return nil, errors.WithContextf(wrapped, "encoding custom coder %v", c)
}
ret := &v1pb.CustomCoder{
Name: c.Name,
Type: t,
Enc: enc,
Dec: dec,
}
return ret, nil
}
func decodeCustomCoder(c *v1pb.CustomCoder) (*coder.CustomCoder, error) {
t, err := decodeType(c.Type)
if err != nil {
return nil, errors.WithContextf(err, "decoding custom coder %v for type %v", c, c.Type)
}
enc, err := decodeUserFn(c.Enc)View on GitHub (pinned to 12126d8942)
Solutions
- Make the decode function a named package-level function
- Replace bound method values with package-level wrapper functions
- Ensure the package defining the decoder is importable by the decoding worker
- Test the coder round-trip locally with EncodeCoderRef/DecodeCoderRef
Example fix
// before
dec := func(b []byte) (T, error) { ... }
c, _ := coder.NewCustomCoder("myCoder", typ, enc, dec)
// after
func decodeT(b []byte) (T, error) { ... }
c, _ := coder.NewCustomCoder("myCoder", typ, encodeT, decodeT) Defensive patterns
Strategy: type-guard
Validate before calling
func isNamedFunc(f interface{}) bool {
name := reflectx.FunctionName(f)
return name != "" && !strings.Contains(name, "func") && !strings.Contains(name, "-")
}
// validate: isNamedCoder(c.Dec) Type guard
func isNamedFunc(f interface{}) bool {
name := reflectx.FunctionName(f)
return name != "" && !strings.Contains(name, "func") && !strings.Contains(name, "-")
} Try / catch
ref, err := graphx.EncodeCoderRef(c)
if err != nil && strings.Contains(err.Error(), "bad decoding function") {
return fmt.Errorf("coder %v uses non-serializable dec fn: %w", c, err)
} Prevention
- Use named top-level functions for coder Dec
- Avoid closures/method values in coders
- Test EncodeCoderRef on all custom coders in CI
- Keep coder packages importable by workers
When it happens
Trigger: coder.NewCustomCoder created with an anonymous or closure decode function (or a bound method), then serialized through Add or EncodeCoderRef — encodeUserFn(c.Dec) fails.
Common situations: Inline func literals as decode callbacks; decoder functions defined inside main or tests where the decoder binary can't resolve them; refactor moved the function so the recorded name no longer resolves.
Related errors
- varint too long
- failed to marshal custom coder %v
- custom coders must be length prefixed: %+v
- bad encoding function
- value %v not encodable with %v
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/09ea0549eb1d2488.
Report an issue: GitHub.