{"record":{"id":"e2e565f51c22f02c","repo":"googleapis/mcp-toolbox","slug":"tool-type-q-already-registered-e2e565","errorCode":null,"errorMessage":"tool type %q already registered","messagePattern":"tool type %q already registered","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/tools/databaseinsights/getadvancedaggregatedwaiteventstats/getadvancedaggregatedwaiteventstats.go","lineNumber":35,"sourceCode":"import (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\n\tyaml \"github.com/goccy/go-yaml\"\n\t\"github.com/googleapis/mcp-toolbox/internal/sources\"\n\t\"github.com/googleapis/mcp-toolbox/internal/sources/databaseinsights\"\n\t\"github.com/googleapis/mcp-toolbox/internal/tools\"\n\t\"github.com/googleapis/mcp-toolbox/internal/util\"\n\t\"github.com/googleapis/mcp-toolbox/internal/util/parameters\"\n)\n\nconst resourceType string = \"databaseinsights-get-advanced-aggregated-wait-event-stats\"\n\nfunc init() {\n\tif !tools.Register(resourceType, newConfig) {\n\t\tpanic(fmt.Sprintf(\"tool type %q already registered\", resourceType))\n\t}\n}\n\nfunc newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {\n\tactual := Config{ConfigBase: tools.ConfigBase{Name: name}}\n\tif err := decoder.DecodeContext(ctx, &actual); err != nil {\n\t\treturn nil, err\n\t}\n\treturn actual, nil\n}\n\ntype compatibleSource interface {\n\tFetchWaitEventStats(ctx context.Context, req *databaseinsights.FetchWaitEventStatsRequest) (*databaseinsights.FetchWaitEventStatsResponse, error)\n}\n\ntype Config struct {\n\ttools.ConfigBase `yaml:\",inline\"`\n\tType             string                 `yaml:\"type\" validate:\"required\"`","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/databaseinsights/getadvancedaggregatedwaiteventstats/getadvancedaggregatedwaiteventstats.go#L17-L53","documentation":"This panic is thrown by the package-level init() of internal/tools/databaseinsights/getadvancedaggregatedwaiteventstats when tools.Register(resourceType, newConfig) returns false. Register returns false only if the tool type key \"databaseinsights-get-advanced-aggregated-wait-event-stats\" already exists in the global registry, meaning two registrations raced or the same package/type was registered twice. It is a deliberate fail-fast to prevent silent tool overwrites.","triggerScenarios":"Calling tools.Register twice with the same resourceType (duplicate const value across files/packages, a package linked twice via conflicting module versions, or a test/helper that re-runs registration code).","commonSituations":"Copy-pasting a tool package and forgetting to change the resourceType const; merging branches that both introduce the same tool type; import cycles or vendoring that pull two copies of the package into the build; test harnesses that initialize the registry more than once.","solutions":["Grep the repo for \"databaseinsights-get-advanced-aggregated-wait-event-stats\" and remove or rename the duplicate resourceType declaration.","Ensure each tool package has a unique resourceType const (follow kebab-case <source>-<tool> convention).","Run `go list -deps ./... | sort | uniq -d` / tidy go.mod so the package is only linked once.","If a test re-triggers init, restructure so registration happens once per process (package init is idempotent per binary; duplicates come from duplicated consts)."],"exampleFix":"// before\nconst resourceType string = \"databaseinsights-get-advanced-aggregated-wait-event-stats\" // duplicated in another file\n// after\nconst resourceType string = \"databaseinsights-get-advanced-aggregated-wait-event-stats-v2\" // or remove the duplicate declaration","handlingStrategy":"validation","validationCode":"const want = \"databaseinsights-get-advanced-aggregated-wait-event-stats\"\nif n := strings.Count(runtime_AllDecls(), want); n > 1 {\n    panic(fmt.Sprintf(\"resourceType %s declared %d times\", want, n))\n}","typeGuard":null,"tryCatchPattern":"// Go panics cannot be caught in normal code; recover only at a boundary:\nfunc safeInit() {\n    defer func() {\n        if r := recover(); r != nil {\n            log.Fatalf(\"tool registration failed: %v\", r)\n        }\n    }()\n    registerTool(resourceType, newConfig)\n}","preventionTips":["Declare each resourceType const in exactly one file per tool package.","Grep the resourceType string before adding a new tool to confirm it is unused.","Follow the <source>-<tool> kebab-case naming convention so keys never collide.","Keep tool registration solely in package init(); never call Register again from tests or bootstrap.","Run `go build ./...` and startup smoke tests in CI to catch duplicate-registry panics early."],"tags":["go","panic","init","tool-registry","duplicate-registration"],"backgroundTag":"duplicate-tool-type-registration","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}