apache/beam · error
Init hooks have already run. Register function during init()
Error message
Init hooks have already run. Register function during init() instead.
What it means
runtime.RegisterFunction caches a function value by its reflect-based symbol name so serialized pipelines can resolve it in workers where the symbol table is unavailable. Registration is only safe before beam.Init() runs; afterward the cache is considered frozen and the library panics to prevent nondeterministic late registration.
Source
Thrown at sdks/go/pkg/beam/core/runtime/symbols.go:78
if r, err := symtab.New(os.Args[0]); err == nil {
return r
}
return failResolver(false)
}
// SymbolResolver resolves a symbol to an unsafe address.
type SymbolResolver interface {
// Sym2Addr returns the address pointer for a given symbol.
Sym2Addr(string) (uintptr, error)
}
// RegisterFunction allows function registration. It is beneficial for performance
// and is needed for functions -- such as custom coders -- serialized during unit
// tests, where the underlying symbol table is not available. It should be called
// in `init()` only.
func RegisterFunction(fn any) {
if initialized {
panic("Init hooks have already run. Register function during init() instead.")
}
key := reflectx.FunctionName(fn)
// If the function was registered already, the key and value will be the same anyway.
cache[key] = fn
}
// RegisterFunctionWithName registers fn under the given name,
// overriding the automatically derived symbol name. This is necessary
// for closures produced by Go generic functions where multiple type
// instantiations generate closures with the same compiler-assigned
// name (e.g. "pkg.Func[...].func1") — without distinct names the
// last registration wins and cross-worker deserialization resolves
// the wrong function.
//
// Callers must ensure that name is stable across process invocations
// (pipeline driver and workers must agree). A typical choice is
// "<package>.<GenericFunc>[<TypeParam>].enc".View on GitHub (pinned to 12126d8942)
Solutions
- Move all RegisterFunction calls into package init() functions.
- Ensure beam.Init() is called only after every package whose init() performs registration is linked/imported.
- In tests, register custom coders/functions via init() or separate registration-capable test helpers that run before Init.
Example fix
// before
func main() {
beam.Init()
runtime.RegisterFunction(myFn) // panics
}
// after
func init() {
runtime.RegisterFunction(myFn)
}
func main() {
beam.Init()
} Defensive patterns
Strategy: validation
Validate before calling
// ensure call site is a package init() function:
func init() { runtime.RegisterFunction(myFn) } Prevention
- Do all symbol registration in init() functions
- Import registering packages before main calls beam.Init()
- Register coders/DoFns at link time, not at first-use time
When it happens
Trigger: Calling runtime.RegisterFunction(fn) (directly or via RegisterDoFn/RegisterCoder wrappers) after beam.Init() has executed — e.g. registering coders/DoFns inside main after Init, in a test after TestMain ran Init, or from lazily initialized code.
Common situations: Tests that call beam.Init() in TestMain and register custom coders inside individual tests; moving Init to the top of main ahead of init()-time registration; plugins loaded at runtime that try to register symbols.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Init hooks have already run. Register type during init() ins
- invalid registration type: %v
- '{type(self).__name__}' not registered as Specifiable. Decor
- empty type
- trigger.AfterCount(%v) must be a positive integer
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/04fc87ec285c3013.
Report an issue: GitHub.