googleapis/mcp-toolbox · critical
tool type %q already registered
Error message
tool type %q already registered
What it means
This is a fail-fast panic raised during package initialization. Each tool package in the toolbox registers its config factory under a unique `resourceType` string (here "dataplex-generate-data-insights") in the global toolRegistry via tools.Register() (internal/tools/tools.go:43). Register() returns false when the type string is already present in the map, and the package's init() deliberately panics because a duplicate registration means two packages are competing for the same tool type key, which would silently break YAML config decoding.
Source
Thrown at internal/tools/dataplex/dataplexgeneratedatainsights/dataplexgeneratedatainsights.go:34
import (
"context"
"fmt"
"net/http"
"github.com/goccy/go-yaml"
"github.com/googleapis/mcp-toolbox/internal/sources"
"github.com/googleapis/mcp-toolbox/internal/tools"
"github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexcommon"
"github.com/googleapis/mcp-toolbox/internal/util"
"github.com/googleapis/mcp-toolbox/internal/util/parameters"
)
const resourceType string = "dataplex-generate-data-insights"
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 {
ProjectID() string
GenerateDataInsights(ctx context.Context, location, resourcePath string, publish bool) (string, error)
}
type Config struct {
tools.ConfigBase `yaml:",inline"`View on GitHub (pinned to 8cc6e09de2)
Solutions
- Search the repo for "dataplex-generate-data-insights" (grep -rn 'dataplex-generate-data-insights') and find the two packages registering it; give the new tool a unique resourceType (e.g. kebab-case tool name per conventions).
- Remove the stale/duplicate import or delete the obsolete tool package that no longer should be registered.
- If the collision is with a forked/renamed package, ensure only one copy of the tool package exists on the module path and go.mod doesn't vendor both old and new versions.
- Rebuild with `go build ./...` and run `go test ./internal/tools/...` to confirm the panic is gone.
Example fix
// before (copied tool, duplicate type) const resourceType string = "dataplex-generate-data-insights" // after (unique type for the new tool) const resourceType string = "dataplex-generate-data-insights-report"
Defensive patterns
Strategy: validation
Validate before calling
// Before building, assert the type string is unique across the repo:
// grep -rn '"dataplex-generate-data-insights"' --include='*.go' . | grep 'resourceType\|tools.Register'
// Expect exactly ONE registration. In Go you can also guard a custom tool's init():
func init() {
if !tools.Register(resourceType, newConfig) {
fmt.Printf("WARNING: %s already registered; skipping duplicate init\n", resourceType)
return // or panic, matching project convention
}
} Type guard
func isRegistered(resourceType string) bool {
_, exists := registrySnapshot[resourceType] // expose registry contents in a test helper
return exists
} Try / catch
// Go panics in init() cannot be recovered within init itself; recover at the top of main if you must:
func main() {
defer func() {
if r := recover(); r != nil {
if s, ok := r.(string); ok && strings.Contains(s, "already registered") {
log.Fatalf("duplicate tool registration: %s", s)
}
panic(r)
}
}()
rootCmd.Execute()
} Prevention
- When copying an existing tool package as a template, change resourceType (and the package name) as the very first edit.
- Add a unit test that iterates all tool packages and asserts every resourceType is unique.
- Name the constant after the kebab-case tool type per project convention so collisions are obvious in review.
- Run `go build ./...` and `go vet` in CI before merging; the panic fires at init so any import-graph mistake surfaces immediately.
- grep for the new type string before committing to confirm only one tools.Register call exists.
When it happens
Trigger: Importing (directly or transitively) two packages whose init() functions call tools.Register() with the same resourceType constant "dataplex-generate-data-insights" — typically after a copy-pasted tool package kept the old const, or a rename/re-creation of the tool package left both old and new packages imported by cmd/root.go or a prebuilt-config registration list.
Common situations: A developer copies an existing tool directory (e.g. another dataplex tool) to start a new tool and forgets to change the resourceType constant; a merge/rebase re-adds an import that was removed; a fork adds a tool with a colliding type string; blank-importing two tool registration files that both claim the type.
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/e0b256595f2f3e5c.
Report an issue: GitHub.