apache/beam · error

bad encoding function

Error message

bad encoding function

What it means

encodeCustomCoder serializes a coder.CustomCoder, calling encodeUserFn on its encoding function c.Enc. If the encoding function cannot be represented as a serializable user function (e.g. it is a closure, a non-top-level function, or defined in a package that cannot be resolved), the error is wrapped as "bad encoding function". Beam requires custom coder functions to be named, top-level functions in a registered package.

Source

Thrown at sdks/go/pkg/beam/core/runtime/graphx/serialize.go:144

		t, err := decodeFullType(out.Type)
		if err != nil {
			wrapped := errors.Wrap(err, "bad output type")
			return "", nil, nil, nil, nil, errors.WithContextf(wrapped, "decoding userfn %v", edge)
		}
		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) {

View on GitHub (pinned to 12126d8942)

Solutions

  1. Define the encoding function as a named, package-level function
  2. Avoid closures, bound methods, and anonymous funcs in custom coders
  3. Verify encodeUserFn can resolve the function's fully qualified name (use reflectx.FunctionName to test)
  4. Move coder functions out of test/generated code into a stable package

Example fix

// before
enc := func(v T) []byte { ... }
c, _ := coder.NewCustomCoder("myCoder", typ, enc, dec)
// after
func encodeT(v T) []byte { ... }
c, _ := coder.NewCustomCoder("myCoder", typ, encodeT, decodeT)
Defensive patterns

Strategy: type-guard

Validate before calling

func isValidCoderFn(f interface{}) bool {
	v := reflect.ValueOf(f)
	return v.Kind() == reflect.Func && v.Pointer() != 0 &&
		runtime.IsFunction(v) && !isClosureOrMethodValue(v)
}

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 encoding function") {
	return fmt.Errorf("coder %v uses non-serializable enc fn: %w", c, err)
}

Prevention

When it happens

Trigger: Creating a coder.NewCustomCoder whose enc function is an anonymous/closure function, a method value, or a function in an internal/unregistered package, then calling Add or EncodeCoderRef to serialize the coder.

Common situations: Passing inline func literals to NewCustomCoder; using methods bound to an instance; defining coders in test binaries or generated code that cannot be referenced by name at decode time.

Related errors


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