{"record":{"id":"09f60dd59d7f96f8","repo":"googleapis/mcp-toolbox","slug":"tool-type-q-already-registered-09f60d","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/postgreslongrunningtransactions/postgreslongrunningtransactions.go","lineNumber":62,"sourceCode":"        wait_event_type,\n        wait_event,\n        query\n    FROM\n        pg_stat_activity\n    WHERE\n        state <> 'idle'\n        AND (now() - xact_start) > COALESCE($1::INTERVAL, interval '5 minutes')\n        AND xact_start IS NOT NULL\n        AND pid <> pg_backend_pid()\n    ORDER BY\n        xact_age DESC\n    LIMIT\n        COALESCE($2::int, 20);\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":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/postgres/postgreslongrunningtransactions/postgreslongrunningtransactions.go#L44-L80","documentation":"This panic comes from the package-level init() of the postgres-long-running-transactions tool package. tools.Register() stores tool constructors in a global registry keyed by type string; returning false means \"postgres-long-running-transactions\" was already registered, so init() panics. The failure happens at process startup, before any YAML tool config is decoded.","triggerScenarios":"Linking a binary where two packages register \"postgres-long-running-transactions\": a duplicated postgreslongrunningtransactions package under a second import path, or an edited resourceType in another package colliding with it (postgreslongrunningtransactions.go:30).","commonSituations":"Copying the long-running-transactions tool as a template and forgetting to change resourceType; fork/vendor duplication; merge conflicts leaving two inits; internal forks that register a patched version alongside upstream.","solutions":["Search the tree for \"postgres-long-running-transactions\" and remove or rename the second registration.","Change the resourceType in any copied package to a unique value.","Deduplicate go.mod replaces / vendor copies and rebuild with a clean cache.","Confirm via fresh checkout that local edits are not introducing the collision."],"exampleFix":"// before\nconst resourceType string = \"postgres-long-running-transactions\" // duplicated\n\n// after\n// stock package unchanged; new package:\nconst resourceType string = \"postgres-stuck-transactions\"","handlingStrategy":"validation","validationCode":"// Validate uniqueness before launching the server:\nconst key = \"postgres-long-running-transactions\"\nif countRegistrations(key) != 1 { // from your build-time registry scan\n    return fmt.Errorf(\"expected exactly one registration for %q\", key)\n}","typeGuard":"func uniqueKey(rt string, all map[string][]string) string {\n    if len(all[rt]) > 1 {\n        panic(\"duplicate resourceType: \" + strings.Join(all[rt], \", \"))\n    }\n    return rt\n}","tryCatchPattern":"func main() {\n    defer func() {\n        if r := recover(); r != nil {\n            fmt.Fprintf(os.Stderr, \"startup panic, check for duplicate tool registration: %v\\n\", r)\n            os.Exit(1)\n        }\n    }()\n    // server startup that links tool packages\n}","preventionTips":["Rename resourceType immediately when template-copying a tool package.","Run unit tests (`go test ./internal/tools/...`) which exercise init and surface duplicate panics early.","Keep tool packages under a single import path; avoid module replace tricks that duplicate them.","Grep for the intended key before assigning it to a new tool."],"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"}