vitessio/vitess · error
symbol NewAuthenticator must be of type `func() Authenticato
Error message
symbol NewAuthenticator must be of type `func() Authenticator`; have %T
What it means
vtadmin loads RBAC authenticator implementations as Go plugins via the plugin package, expecting the plugin to export a symbol `NewAuthenticator` with the exact type `func() Authenticator`. If the symbol exists but has a different signature (including non-zero parameters or different return types), the plugin load fails with this type assertion error. This is how vtadmin enforces the authenticator constructor contract at plugin load time.
Source
Thrown at go/vt/vtadmin/rbac/authentication.go:163
defer authenticatorsM.Unlock()
if f, ok := authenticators[path]; ok {
return f(), nil
}
p, err := plugin.Open(path)
if err != nil {
return nil, err
}
sym, err := p.Lookup("NewAuthenticator")
if err != nil {
return nil, err
}
f, ok := sym.(func() Authenticator)
if !ok {
return nil, fmt.Errorf("symbol NewAuthenticator must be of type `func() Authenticator`; have %T", sym)
}
authenticators[path] = f
return f(), nil
}
View on GitHub (pinned to 01a25a7d17)
Solutions
- Change the plugin to export `func NewAuthenticator() rbac.Authenticator` exactly — no parameters, single interface return.
- Configure constructor arguments through plugin state or environment instead of constructor parameters.
- Rebuild the plugin against the exact same version/commit of the vtadmin rbac package the host binary uses so interface types are identical.
- Verify with `nm` or `go plugin` inspection that NewAuthenticator is exported and its symbol type matches.
Example fix
// before
func NewAuthenticator(cfg map[string]string) *MyAuth { ... }
// after
func NewAuthenticator() rbac.Authenticator {
cfg := loadConfigFromEnv()
return &MyAuth{cfg: cfg}
} Defensive patterns
Strategy: type-guard
Validate before calling
p, err := plugin.Open(path)
if err != nil { return err }
sym, err := p.Lookup("NewAuthenticator")
if err != nil { return err }
if _, ok := sym.(func() rbac.Authenticator); !ok {
return fmt.Errorf("plugin %s: NewAuthenticator has wrong signature", path)
} Type guard
func isValidAuthenticatorPlugin(sym plugin.Symbol) bool {
_, ok := sym.(func() Authenticator)
return ok
} Try / catch
auth, err := rbac.NewAuthenticatorFromPath(path)
if err != nil && strings.Contains(err.Error(), "must be of type") {
return fmt.Errorf("plugin %s incompatible: rebuild against host vtadmin version", path)
} Prevention
- Copy the canonical plugin template so NewAuthenticator has signature func() Authenticator.
- Build plugins with the exact same Go version and vtadmin module version as the host binary.
- Smoke-test plugin loading in CI before deploying.
- Pass configuration via env or plugin state, never constructor parameters.
When it happens
Trigger: Building a custom authenticator plugin whose NewAuthenticator has parameters (e.g., func(cfg map[string]string) Authenticator), returns a concrete type rather than the Authenticator interface, or returns (Authenticator, error).
Common situations: Writing a new plugin after copying an example with a different constructor signature; compiling against a different version of the vtadmin rbac package so the Authenticator interface type differs between plugin and host; exporting the wrong symbol type entirely.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- %w %s
- unregistered Authenticator implementation
- %w: cannot create schema migration in %s
- %w: cannot cancel schema migration in %s
- %w: cannot cleanup schema migration in %s
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/9a1417e75e4a0874.
Report an issue: GitHub.