{"record":{"id":"99c46fd35815d89b","repo":"googleapis/mcp-toolbox","slug":"tool-type-q-already-registered-99c46f","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/postgresgetcolumncardinality/postgresgetcolumncardinality.go","lineNumber":53,"sourceCode":"          ROUND(\n              CASE\n                  WHEN s.n_distinct < 0 THEN ABS(s.n_distinct) * c.reltuples\n                  ELSE s.n_distinct\n              END\n          ) AS estimated_cardinality\n      FROM pg_stats s\n      JOIN pg_class c ON s.tablename = c.relname\n      JOIN pg_namespace n ON c.relnamespace = n.oid\n      WHERE s.schemaname = $1 -- Parameter for schema\n        AND s.tablename = $2 -- Parameter for table\n        AND n.nspname = $1  -- (same parameter as schema)\n        AND s.attname = COALESCE($3, s.attname)\n      ORDER BY estimated_cardinality DESC;\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":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/postgres/postgresgetcolumncardinality/postgresgetcolumncardinality.go#L35-L71","documentation":"This is a startup panic from postgresgetcolumncardinality's init(): tools.Register (internal/tools/tools.go:43) returned false because the tool registry map already contains a factory for the package's `resourceType` string (\"postgres-get-column-cardinality\"). Duplicate type strings make config decoding ambiguous, so the code panics at package-init time rather than allowing a silent overwrite.","triggerScenarios":"Compiling a binary that pulls in two init() calls registering the same postgres tool type — usually a copied postgresgetcolumncardinality.go that kept the original `resourceType` constant, or two declarations of the same constant in the linked package set.","commonSituations":"Duplicating the file to create a customized cardinality query and renaming only the directory/package; a merge keeping old and new copies; importing both the upstream package and an internal fork.","solutions":["Find the collision with rg '\"postgres-get-column-cardinality\"' internal/ cmd/ and change one `resourceType` constant.","If the copy is a deliberate new tool, assign a distinct kebab-case type string and matching package name.","Delete the redundant copy and its import if unintended.","Run `go build ./...` and the postgres tool tests to verify."],"exampleFix":"// before (duplicated tool)\nconst resourceType string = \"postgres-get-column-cardinality\"\n// after\nconst resourceType string = \"postgres-get-column-cardinality-estimated\"","handlingStrategy":"validation","validationCode":"// Assert uniqueness before building:\n//   rg -F '\"postgres-get-column-cardinality\"' internal/ cmd/  # exactly 1 expected\n// CI smoke test that forces init() of every tool package:\nfunc TestInitNoPanics(t *testing.T) {\n\t// must complete without \"tool type already registered\" panic\n}","typeGuard":"func isUniqueRegistration(sourceRoot, resourceType string) bool {\n\treturn countLiteralMatches(sourceRoot, \"\\\"\"+resourceType+\"\\\"\") == 1\n}","tryCatchPattern":null,"preventionTips":["Treat `resourceType` as part of the tool's identity: rename it whenever you fork the tool.","Match the constant to the directory name; a mismatch in review is a red flag.","Keep a CI test that imports all tool packages so duplicate init() registrations fail fast.","Search the tree for the type literal before importing a new tool package.","Avoid keeping both original and copied variants in the module."],"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"}