apache/beam · critical
Invalid signature for {{$upperName}}
Error message
Invalid signature for {{$upperName}} What it means
This panic is raised by the generated buildStartBundleWrapper/buildFinishBundleWrapper functions in Apache Beam Go's register package (rendered from register.tmpl). The DoFn has a StartBundle or FinishBundle method (found via reflect.MethodByName), but its signature's number of outputs (or number of inputs) doesn't fall into any supported case in the generated switch — only 0 or 1 outputs and up to startFinishBundleMaxIn inputs are supported. The library panics because it cannot build a wrapper for an unsupported method shape.
Source
Thrown at sdks/go/pkg/beam/register/register.tmpl:37
{{$lowerName}}Out := -1
{{$lowerName}}Method := reflect.ValueOf(doFn).MethodByName("{{$upperName}}")
if !{{$lowerName}}Method.IsValid() {
return nil
}
{{$lowerName}}In = {{$lowerName}}Method.Type().NumIn()
{{$lowerName}}Out = {{$lowerName}}Method.Type().NumOut()
switch {
{{range $funcIn := upto $startFinishBundleMaxIn}}
case {{$lowerName}}In == {{$funcIn}}:
switch { {{range $funcOut := upto 2}}{{$possibleCombos := (possibleBundleLifecycleParameterCombos $funcIn $numParams)}}{{if $possibleCombos}}
case {{$lowerName}}Out == {{$funcOut}}:
{{$first := true}}{{range $funcCombo := $possibleCombos}}{{if $first}}{{$first = false}} {{else}} else {{end}}if _, ok := doFn.({{$lowerName}}{{$funcIn}}x{{$funcOut}}{{if (or $funcIn $funcOut)}}[{{(join $funcCombo ", ")}}{{if $funcOut}}{{if $funcIn}}, {{end}}error{{end}}]{{end}}); ok {
return register{{$upperName}}{{$funcIn}}x{{$funcOut}}FuncAndMakeStructWrapper{{if (or $funcIn $funcOut)}}[{{(join $funcCombo ", ")}}{{if $funcOut}}{{if $funcIn}}, {{end}}error{{end}}]{{end}}()
} {{end}}{{end}}else {
panic("Unable to infer the types of {{$upperName}}")
}{{end}}
default:
panic("Invalid signature for {{$upperName}}")
}
{{end}}
default:
panic("Invalid signature for {{$upperName}}")
}
}
{{end}}{{define "BuildWrapper_SetupTeardown"}}
{{$lowerName := "unknown"}}{{$upperName := "unknown"}}{{if (eq .func "setup")}}{{$lowerName = "setup"}}{{$upperName = "Setup"}}{{end}}{{if (eq .func "teardown")}}{{$lowerName = "teardown"}}{{$upperName = "Teardown"}}{{end}}
func build{{$upperName}}Wrapper(doFn any) func(any) reflectx.Func {
if _, ok := doFn.({{$lowerName}}0x0); ok {
{{$lowerName}}Caller := func(fn any) reflectx.Func {
f := fn.(func())
return &caller0x0{fn: f}
}
reflectx.RegisterFunc(reflect.TypeOf((*func())(nil)).Elem(), {{$lowerName}}Caller)
return func(fn any) reflectx.Func {
return reflectx.MakeFunc(func() {View on GitHub (pinned to 12126d8942)
Solutions
- Change StartBundle/FinishBundle to return at most one value (either nothing or error): e.g. func (fn *MyFn) FinishBundle() error.
- Reduce lifecycle method parameters to the supported set (none, or context.Context and/or the accumulator types matching ProcessElement combos).
- Move extra logic/returns out of the lifecycle method into ProcessElement or struct state.
- Check the Beam version's supported signature table in sdks/go/pkg/beam/core/graph/coder or register docs and conform to it.
- If a method genuinely isn't a lifecycle hook, rename it so reflect.MethodByName doesn't pick it up.
Example fix
// before: two outputs unsupported
type MyFn struct{}
func (fn *MyFn) StartBundle(ctx context.Context) (bool, error) { return true, nil }
// after: at most one output
type MyFn struct{}
func (fn *MyFn) StartBundle(ctx context.Context) error { return nil } Defensive patterns
Strategy: validation
Validate before calling
// Pre-check outputs/inputs of lifecycle methods before registration:
func checkSig(fn any, method string, maxIn int) error {
m := reflect.ValueOf(fn).MethodByName(method)
if !m.IsValid() {
return nil
}
t := m.Type()
if t.NumIn() > maxIn {
return fmt.Errorf("%s: %d inputs exceeds max %d", method, t.NumIn(), maxIn)
}
if t.NumOut() > 1 {
return fmt.Errorf("%s: %d outputs exceeds max 1", method, t.NumOut())
}
return nil
} Type guard
func hasSupportedFinishBundle(fn any) bool {
_, ok := fn.(interface{ FinishBundle() })
_, okE := fn.(interface{ FinishBundle() error })
return ok || okE
} Try / catch
func registerSafe(fn any) {
defer func() {
if r := recover(); r != nil {
log.Fatalf("unsupported DoFn signature (fix StartBundle/FinishBundle/Setup/Teardown shape): %v", r)
}
}()
register.DoFn2x1(fn)
} Prevention
- Never return more than one value (or one error) from lifecycle methods.
- Keep lifecycle inputs to the documented set: none, context.Context, and/or accumulator types.
- Add CI tests that call the registration path for all DoFns to catch signature regressions.
- After upgrading Beam, re-run registration tests since supported arities are template-configured.
When it happens
Trigger: Registering a DoFn whose StartBundle/FinishBundle method returns 2+ values (e.g. func(context.Context) (T0, T1, error)), or takes more inputs than the configured maximum (startFinishBundleMaxIn, driven by ProcessElement max params); also the outer default case when NumIn exceeds all enumerated cases.
Common situations: Porting DoFns from other Beam SDKs that allow richer lifecycle signatures; adding extra return values like (bool, error) for control flow; auto-generated DoFn wrappers with bloated signatures; newer Beam versions raising/lowering the supported arity so previously-working code panics after upgrade.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- may only have a single context.Context parameter and it must
- may only have a single PaneInfo parameter and it must preced
- may only have a single Window parameter and it must precede
- may only have a single beam.EventTime parameter and it must
- failed to construct userfn
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/b78ca9e4d9368ca8.
Report an issue: GitHub.