{"record":{"id":"883daf822ba1be07","repo":"googleapis/mcp-toolbox","slug":"tool-type-q-already-registered-883daf","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/postgreslistdatabasestats/postgreslistdatabasestats.go","lineNumber":100,"sourceCode":"            -- Exclude template databases if not requested\n            AND ( $2::boolean IS TRUE OR d.datistemplate IS FALSE )\n    )\n    SELECT *\n    FROM database_stats\n    WHERE\n        ($1::text IS NULL OR database_name LIKE '%' || $1::text || '%')\n        AND ($3::text IS NULL OR database_owner LIKE '%' || $3::text || '%')\n        AND ($4::text IS NULL OR default_tablespace LIKE '%' || $4::text || '%')\n    ORDER BY\n        CASE WHEN $5::text = 'size' THEN database_size_bytes END DESC,\n        CASE WHEN $5::text = 'commit' THEN xact_commit END DESC,\n        database_name\n    LIMIT COALESCE($6::int, 10);\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":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/postgres/postgreslistdatabasestats/postgreslistdatabasestats.go#L82-L118","documentation":"This is a startup panic from postgreslistdatabasestats' init(): tools.Register (internal/tools/tools.go:43) returned false because a factory for the same resourceType string (\"postgres-list-database-stats\") already exists in the shared toolRegistry map. Duplicate registrations would make a config's `type` field ambiguous, so the library panics during package initialization, before main() executes.","triggerScenarios":"Building a binary that imports two packages whose init() both call tools.Register with \"postgres-list-database-stats\" — typically a copied postgreslistdatabasestats.go retaining the original constant, or two files declaring that same constant.","commonSituations":"Duplicating the stats tool to tweak the SQL (different LIMIT, extra filters) while renaming only the package; a merge that preserved duplicate directories; vendoring a fork and importing both it and the original.","solutions":["rg '\"postgres-list-database-stats\"' internal/ cmd/ to find the second registration and change one `resourceType` constant.","For an intentional variant, choose a distinct kebab-case type (e.g. \"postgres-list-database-stats-detailed\") and matching package name.","Remove the unintended duplicate package/import.","Verify with `go build ./...` and unit tests that init completes."],"exampleFix":"// before (tweaked copy)\nconst resourceType string = \"postgres-list-database-stats\"\n// after\nconst resourceType string = \"postgres-list-database-stats-detailed\"","handlingStrategy":"validation","validationCode":"// Assert the type string is declared exactly once before building:\n//   rg -F '\"postgres-list-database-stats\"' internal/ cmd/   # expect 1 hit\n// Link-all smoke test:\nfunc TestInitNoDuplicateRegistration(t *testing.T) {\n\t// importing all internal/tools packages must not panic\n}","typeGuard":"func hasSingleRegistration(root, resourceType string) bool {\n\treturn countLiteralMatches(root, \"\\\"\"+resourceType+\"\\\"\") == 1\n}","tryCatchPattern":null,"preventionTips":["Copy-paste rule: first edit after duplicating a tool is renaming `resourceType`.","Keep the constant in sync with the package/directory name to spot drift in review.","Add a CI smoke test that triggers all tool init()s.","Run a literal search for the type string before importing new/forked packages.","Remove superseded duplicates rather than leaving both importable."],"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"}