{"record":{"id":"a7b4a8e4c4666db0","repo":"googleapis/mcp-toolbox","slug":"tool-type-q-already-registered-a7b4a8","errorCode":null,"errorMessage":"tool type %q already registered","messagePattern":"tool type %q already registered","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/tools/postgres/postgreslistviews/postgreslistviews.go","lineNumber":55,"sourceCode":"            viewowner AS owner_name,\n            definition\n        FROM pg_views\n    )\n    SELECT *\n    FROM list_views\n    WHERE\n        schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')\n        AND schema_name NOT LIKE 'pg_temp_%'\n        AND ($1::text IS NULL OR view_name ILIKE '%' || $1::text || '%')\n        AND ($2::text IS NULL OR schema_name ILIKE '%' || $2::text || '%')\n    ORDER BY\n        schema_name, view_name\n    LIMIT COALESCE($3::int, 50);\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/postgreslistviews/postgreslistviews.go#L37-L73","documentation":"This panic comes from the package-level init() of the postgres-list-views tool package. The tools.Register() call maintains a global map of tool type strings to config constructors and returns false if the key already exists, i.e. \"postgres-list-views\" was registered twice. Since registration happens in init(), the panic aborts the binary at startup.","triggerScenarios":"A build containing two registrations for \"postgres-list-views\": a duplicated postgreslistviews package under a different import path (vendor/fork), or a modified resourceType in another postgres tool package colliding with postgres-list-views (postgreslistviews.go:30).","commonSituations":"Template-copying postgreslistviews.go to create a new listing tool without changing resourceType; duplicate vendoring; merge conflict residue; custom prebuilt configs compiled into a binary that also links the stock package under a second path.","solutions":["Grep for \"postgres-list-views\" to find every registration and delete/rename the duplicate.","Give a copied package a unique kebab-case resourceType.","Remove duplicate import paths / vendor copies and rebuild.","Check git diff for accidental duplicate init() or constant edits before building."],"exampleFix":"// before\nconst resourceType string = \"postgres-list-views\" // registered twice\n\n// after\n// stock package keeps \"postgres-list-views\";\n// new package:\nconst resourceType string = \"postgres-list-materialized-views\"","handlingStrategy":"validation","validationCode":"// Startup validation in main() (for forks that control registration):\nkey := \"postgres-list-views\"\nif _, exists := registeredKeys[key]; exists {\n    return fmt.Errorf(\"refusing to start: %q registered twice\", key)\n}","typeGuard":"func isDuplicate(key string, seen map[string]bool) bool {\n    if seen[key] { return true }\n    seen[key] = true\n    return false\n}","tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        log.Fatalf(\"tool registration panic (duplicate resourceType?): %v\", r)\n    }\n}() // wrap the code path that first imports tool packages","preventionTips":["Use a naming convention (postgres-<entity>-<action>) and check it before adding a new key.","Add a lint/test that asserts unique resourceType constants across internal/tools.","Avoid vendor/fork duplication of postgres tool packages.","Review merge conflicts in tool packages for leftover duplicate init() blocks."],"tags":["go","init","panic","tool-registry","duplicate-registration"],"backgroundTag":"duplicate-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"}