grafana/k6 · critical
extension already registered: %s
Error message
extension already registered: %s
What it means
ext.Register keeps a per-type map keyed by extension name. Registering a second extension of the same type under an already-used name trips this panic during init, before any test runs — the registry deliberately refuses silent overrides so two different modules cannot masquerade under one import path.
Source
Thrown at ext/ext.go:72
func (e Extension) String() string {
return fmt.Sprintf("%s %s, %s [%s]", e.Path, e.Version, e.Name, e.Type)
}
// Register a new extension with the given name and type. This function will
// panic if an unsupported extension type is provided, or if an extension of the
// same type and name is already registered.
func Register(name string, typ ExtensionType, mod any) {
mx.Lock()
defer mx.Unlock()
exts, ok := extensions[typ]
if !ok {
panic(fmt.Sprintf("unsupported extension type: %T", typ))
}
if _, ok := exts[name]; ok {
panic(fmt.Sprintf("extension already registered: %s", name))
}
path, version := extractModuleInfo(mod)
exts[name] = &Extension{
Name: name,
Type: typ,
Module: mod,
Path: path,
Version: version,
}
}
// Get returns all extensions of the specified type.
func Get(typ ExtensionType) map[string]*Extension {
mx.RLock()
defer mx.RUnlock()
View on GitHub (pinned to 93accf6570)
Solutions
- Give each extension a unique name — conventionally its full import path (k6/x/...), which makes collisions structurally unlikely
- Remove the stale copy: go mod tidy / clean the xk6 build pins so only one version of the module is linked
- If the duplicate comes from a vendored fork, rename the fork's registered name in its init()
- Rebuild the binary and verify with k6 version / k6 inspect that each extension appears once
Example fix
// before (two packages both do)
func init() { ext.Register("k6/x/queue", ext.JSExtension, &Q{}) } // second one panics at startup
// after (fork renamed)
func init() { ext.Register("k6/x/queue-fork", ext.JSExtension, &Q{}) } Defensive patterns
Strategy: validation
Validate before calling
// Go, guard before registering
func registerOnce(name string, typ ext.ExtensionType, mod any) {
if _, dup := ext.Get(typ)[name]; dup { // note: panics on bad typ; validate it first
panic(fmt.Sprintf("extension %q already registered - rename this one", name))
}
ext.Register(name, typ, mod)
} Prevention
- Register under the full import path (k6/x/...) — globally unique by construction
- go mod tidy after adding/removing extensions so stale duplicates drop out
- When vendoring a fork, rename its registered module name immediately
- Inspect registration output (k6 version / k6 inspect) in CI to catch double registrations before users do
When it happens
Trigger: Two xk6 extensions in one binary both calling ext.Register("k6/x/mymod", ext.JSExtension, ...); an extension registered from two init() paths (e.g. the package linked twice via different import paths after a rename); a fork reusing an existing extension's name; output extensions colliding on a name like "custom".
Common situations: k6 binary built with two extensions where one is a vendored fork/older copy of the other (common after xk6 build flag drift); module moved to a new path but old path still imported somewhere; accidental double registration when a file is duplicated in the extensions tree; CI building with stale extension pins.
Related errors
- unsupported extension type: %T
- Error while parsing selector `${selector}` - cannot use ${op
- unhandled extension type: %s
- Error while parsing selector `${selector}` - selector cannot
- "${attr}" attribute is only supported for roles: ${roles.sli
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/a1c3e9c28b22b034.
Report an issue: GitHub.