{"record":{"id":"539c80bd0b199fd3","repo":"googleapis/mcp-toolbox","slug":"tool-type-q-already-registered-539c80","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/mysql/mysqlsql/mysqlsql.go","lineNumber":34,"sourceCode":"\nimport (\n\t\"context\"\n\t\"database/sql\"\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/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 = \"mysql-sql\"\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\tMySQLPool() *sql.DB\n\tRunSQL(context.Context, string, []any) (any, error)\n}\n\ntype Config struct {\n\ttools.ConfigBase   `yaml:\",inline\"`","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/mysql/mysqlsql/mysqlsql.go#L16-L52","documentation":"This is a fail-fast panic raised during package initialization. The mysqlsql package registers its config factory under resourceType \"mysql-sql\" in the global toolRegistry via tools.Register() (internal/tools/tools.go:43). Register() returns false when the type key already exists, and init() panics because two packages claiming the same YAML `kind` would make tool config decoding ambiguous.","triggerScenarios":"Another init() also registers \"mysql-sql\" — e.g. a copied fork of mysqlsql.go left in the tree, this package imported twice via different module paths, or a custom tool reusing the built-in mysql-sql type string.","commonSituations":"Forking mysqlsql to tweak behavior without renaming the package/type; a vendored copy of the toolbox alongside the direct dependency; rebasing a feature branch that duplicates the package directory.","solutions":["grep -rn '\"mysql-sql\"' to find every tools.Register call with this type; keep exactly one.","If a fork was intended, rename the resourceType (e.g. \"mysql-sql-custom\") and the package directory.","Remove duplicate imports or extra replace directives causing the package to be linked twice.","Run `go build ./...` and `go test ./internal/tools/mysql/mysqlsql/...` to confirm."],"exampleFix":"// before (forked copy)\nconst resourceType string = \"mysql-sql\"\n\n// after\nconst resourceType string = \"mysql-sql-v2\"","handlingStrategy":"validation","validationCode":"// Verify uniqueness before building:\n// grep -rn '\"mysql-sql\"' --include='*.go' . | grep 'resourceType\\|tools.Register'\n// Exactly one match expected. Soft guard for forked tools:\nfunc init() {\n\tif !tools.Register(resourceType, newConfig) {\n\t\tfmt.Printf(\"WARNING: %s already registered; skipping duplicate init\\n\", resourceType)\n\t\treturn\n\t}\n}","typeGuard":"func isRegistered(resourceType string) bool {\n\t_, exists := registrySnapshot[resourceType]\n\treturn exists\n}","tryCatchPattern":"// Recover at the process boundary:\nfunc main() {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tif s, ok := r.(string); ok && strings.Contains(s, \"already registered\") {\n\t\t\t\tlog.Fatalf(\"duplicate tool registration: %s\", s)\n\t\t\t}\n\t\t\tpanic(r)\n\t\t}\n\t}()\n\trootCmd.Execute()\n}","preventionTips":["If forking a built-in tool, always rename resourceType to a non-conflicting string (users' YAML `kind` must change too).","Add a registry-uniqueness test that fails CI on duplicate type strings.","Keep exactly one copy of the toolbox module in go.mod (no overlapping replace directives).","After rebases, run go build ./... to catch duplicated packages early.","Document the tool-type naming convention (kebab-case, product-prefixed) so new tools pick unique names."],"tags":["go","init-panic","tool-registry","duplicate-registration","mysql"],"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"}