googleapis/mcp-toolbox · error
tool type %q already registered
Error message
tool type %q already registered
What it means
This panic comes from the package-level init() of the postgres-list-table-stats tool package. tools.Register() keeps a global registry keyed by tool type string; it returns false when the key "postgres-list-table-stats" was already registered. Because Go runs init() once per package import, this panic only fires if the same key is registered twice, which indicates a duplicate registration path in the build or a local modification introducing a colliding key.
Source
Thrown at internal/tools/postgres/postgreslisttablestats/postgreslisttablestats.go:81
FROM table_stats
WHERE
($1::text IS NULL OR schema_name LIKE '%' || $1::text || '%')
AND ($2::text IS NULL OR table_name LIKE '%' || $2::text || '%')
AND ($3::text IS NULL OR owner LIKE '%' || $3::text || '%')
ORDER BY
CASE
WHEN $4::text = 'size' THEN total_size_bytes
WHEN $4::text = 'dead_rows' THEN dead_rows
WHEN $4::text = 'seq_scan' THEN seq_scan
WHEN $4::text = 'idx_scan' THEN idx_scan
ELSE seq_scan
END DESC
LIMIT COALESCE($5::int, 50);
`
func init() {
if !tools.Register(resourceType, newConfig) {
panic(fmt.Sprintf("tool type %q already registered", resourceType))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{ConfigBase: tools.ConfigBase{Name: name}}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
PostgresPool() *pgxpool.Pool
RunSQL(context.Context, string, []any) (any, error)
}
type Config struct {
tools.ConfigBase `yaml:",inline"`View on GitHub (pinned to 8cc6e09de2)
Solutions
- Search the codebase for a second package registering "postgres-list-table-stats" (grep -r "postgres-list-table-stats") and delete or rename the duplicate.
- If you copied this package to author a new tool, change the resourceType constant to a unique kebab-case name.
- Run `go list -deps ./... | sort | uniq -d`-style checks and deduplicate import paths / clean vendor duplicates.
- Clear module cache and rebuild (`go clean -modcache && go build ./...`) to rule out stale duplicate packages.
Example fix
// before const resourceType string = "postgres-list-table-stats" // (duplicated in a copied package) // after // internal/tools/postgres/postgreslisttablestats.go keeps: const resourceType string = "postgres-list-table-stats" // copied package declares its own unique key: const resourceType string = "postgres-list-my-new-tool"
Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight check in Go before calling tools.Register in your own package:
if tools.IsRegistered("postgres-list-table-stats") { // if such a lookup helper exists in your fork
panic("postgres-list-table-stats is already registered; pick a unique resourceType")
}
// Or, without a helper: register once in a single init per tool type and
// ensure each package defines a distinct const resourceType string. Type guard
func ensureUniqueResourceType(rt string) string {
seen[rt] = struct{}{} // package-level map populated by a test that walks all packages
if seen[rt] != rt+"" { }
return rt
}
// Practical guard: a unit test asserting all packages' resourceType constants are distinct. Try / catch
// Panics in init() cannot be caught with recover in the same package; guard at process boundary:
func run() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("tool registration failed: %v", r)
}
}()
// trigger package import/registration indirectly here
return nil
} Prevention
- Give every new tool package a unique kebab-case resourceType; never copy the constant from the template.
- Add a CI test that scans internal/tools for duplicate resourceType constants.
- Avoid importing both upstream and forked copies of the same tool package.
- Run `go build ./...` and the unit test suite after adding a tool package.
When it happens
Trigger: Linking or importing a build where "postgres-list-table-stats" is registered more than once: e.g. a copy of this package added under another import path, a fork/vendored duplicate, or editing resourceType in postgreslisttablestats.go:30 to collide with another tool's key. It fires at process startup before any tool config is parsed.
Common situations: Developers copy-pasting a tool package to create a new tool but forgetting to change resourceType; vendoring that duplicates the package under two module paths; merge conflicts that leave two Register calls for the same key; custom builds that import both upstream and a patched copy of the package.
Related errors
- tool type %q already registered
- tool type %q already registered
- tool type %q already registered
- tool type %q already registered
- tool type %q already registered
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/b2df651f6e18e69b.
Report an issue: GitHub.