grafana/k6 · critical

unsupported extension type: %T

Error message

unsupported extension type: %T

What it means

ext.Register is the API xk6 extensions use at init time to plug modules into k6. The extensions map only has buckets for the four declared ExtensionType constants (JSExtension, OutputExtension, SecretSourceExtension, SubcommandExtension); any other value — including the zero value — finds no bucket and Register panics with the offending type, crashing the binary at startup.

Source

Thrown at ext/ext.go:68

	Name, Path, Version string
	Type                ExtensionType
	Module              any
}

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.

View on GitHub (pinned to 93accf6570)

Solutions

  1. Use only the exported constants: ext.JSExtension, ext.OutputExtension, ext.SecretSourceExtension, ext.SubcommandExtension
  2. If compiling against older k6, guard with a build tag or version check rather than a raw constant
  3. Fail fast in tests: call ext.Get(type) in a unit test so bad values panic in CI, not at user startup
  4. If you genuinely need a new type, add it to the enum in ext/ext.go and rebuild rather than casting

Example fix

// before
func init() { ext.Register("k6/x/myext", ext.ExtensionType(0), &MyExt{}) } // panics: unsupported extension type

// after
func init() { ext.Register("k6/x/myext", ext.JSExtension, &MyExt{}) }
Defensive patterns

Strategy: validation

Validate before calling

// Go, in the extension package
var validTypes = map[ext.ExtensionType]bool{
  ext.JSExtension: true, ext.OutputExtension: true,
  ext.SecretSourceExtension: true, ext.SubcommandExtension: true,
}
func register(name string, typ ext.ExtensionType, mod any) {
  if !validTypes[typ] { panic(fmt.Sprintf("bad extension type %d for %s", typ, name)) }
  ext.Register(name, typ, mod)
}

Type guard

// Go
func isValidExtType(t ext.ExtensionType) bool { return t >= ext.JSExtension && t <= ext.SubcommandExtension }

Prevention

When it happens

Trigger: An extension's init() calls ext.Register('k6/x/foo', myType, mod) where myType is a cast like ext.ExtensionType(0), ext.ExtensionType(99), or a locally-defined constant outside the enum. Since ExtensionType is an untyped-able uint8, accidental integer literals compile fine and panic at runtime during binary startup.

Common situations: Writing a new xk6 extension and passing an untyped constant 0; copy-pasting a Register call and forgetting to change the type; code written against a newer k6 that adds an extension type, compiled into an older k6 where that bucket does not exist; refactor leaving a stale type value.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/7e2de13f7e943ad8. Report an issue: GitHub.