{"record":{"id":"53ccf41b502bd4a4","repo":"googleapis/mcp-toolbox","slug":"tool-type-q-already-registered-53ccf4","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/postgres/postgreslistinstalledextensions/postgreslistinstalledextensions.go","lineNumber":55,"sourceCode":"        pg_get_userbyid(e.extowner) AS owner,\n        c.description AS description\n    FROM\n        pg_catalog.pg_extension e\n    LEFT JOIN\n        pg_catalog.pg_namespace n\n    ON\n        n.oid = e.extnamespace\n    LEFT JOIN\n        pg_catalog.pg_description c\n    ON\n        c.objoid = e.oid\n        AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass\n    ORDER BY 1;\n`\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\tPostgresPool() *pgxpool.Pool\n\tRunSQL(context.Context, string, []any) (any, error)\n}\n\ntype Config struct {\n\ttools.ConfigBase `yaml:\",inline\"`","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/postgres/postgreslistinstalledextensions/postgreslistinstalledextensions.go#L37-L73","documentation":"This is a startup panic from postgreslistinstalledextensions' init(): tools.Register (internal/tools/tools.go:43) returned false, meaning another factory is already registered under the same resourceType string (\"postgres-list-installed-extensions\") in the shared toolRegistry map. The registry intentionally refuses to overwrite existing keys; a duplicate `type` string makes config resolution ambiguous, so the process panics during package initialization before serving requests.","triggerScenarios":"Compiling a binary that imports two packages registering \"postgres-list-installed-extensions\" — usually a duplicated postgreslistinstalledextensions.go with an unchanged `resourceType` constant, or two files in the build declaring the identical constant.","commonSituations":"Copy-pasting the tool to create a variant (e.g. filtering by schema) and renaming only the package; a merge conflict resolved by keeping both copies; importing both the upstream package and an internal fork.","solutions":["Find the collision using rg '\"postgres-list-installed-extensions\"' internal/ cmd/ and change one `resourceType` constant.","For a deliberate variant tool, assign a unique kebab-case type string (e.g. \"postgres-list-installed-extensions-detailed\") and matching package name.","Remove the unintended duplicate package/import.","Confirm with `go build ./...` and unit tests that the toolbox starts."],"exampleFix":"// before (variant copy)\nconst resourceType string = \"postgres-list-installed-extensions\"\n// after\nconst resourceType string = \"postgres-list-installed-extensions-detailed\"","handlingStrategy":"validation","validationCode":"// Verify single registration before building:\n//   rg -F '\"postgres-list-installed-extensions\"' internal/ cmd/  # expect 1 hit\n// CI smoke test that initializes every tool package:\nfunc TestAllToolInits(t *testing.T) {\n\t// must not panic: tool type \"postgres-list-installed-extensions\" already registered\n}","typeGuard":"func registrationIsSingular(root, resourceType string) bool {\n\treturn countLiteralMatches(root, \"\\\"\"+resourceType+\"\\\"\") == 1\n}","tryCatchPattern":null,"preventionTips":["When forking a tool into a new package, rename the `resourceType` constant in the same commit.","Keep `resourceType` matching the tool's kebab-case name and directory.","Add an import-all smoke test to CI so duplicate registrations are caught before release.","Search the tree for the type string literal before importing any new tool package.","Delete dead/forked duplicates promptly."],"tags":["go","init-panic","duplicate-registration","tool-registry","postgres"],"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"}