{"record":{"id":"55f8b32e9800ae74","repo":"googleapis/mcp-toolbox","slug":"tool-type-q-already-registered-55f8b3","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/postgresreplicationstats/postgresreplicationstats.go","lineNumber":52,"sourceCode":"        pid,\n        usename,\n        application_name,\n        backend_xmin,\n        client_addr,\n        state,\n        sync_state,\n        pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn)) AS sent_lag,\n        pg_size_pretty(pg_wal_lsn_diff(sent_lsn, write_lsn)) AS write_lag,\n        pg_size_pretty(pg_wal_lsn_diff(write_lsn, flush_lsn)) AS flush_lag,\n        pg_size_pretty(pg_wal_lsn_diff(flush_lsn, replay_lsn)) AS replay_lag,\n        pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS total_lag\n    FROM\n        pg_stat_replication;\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":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/postgres/postgresreplicationstats/postgresreplicationstats.go#L34-L70","documentation":"This panic comes from the package-level init() of the postgres-replication-stats tool package. tools.Register() registers the config constructor for \"postgres-replication-stats\" in a global registry and returns false if that key already exists, so init() panics on a duplicate. Because init runs at link/startup time, the whole toolbox binary fails immediately.","triggerScenarios":"A build registering \"postgres-replication-stats\" twice: a duplicated postgresreplicationstats package under another import path, or someone changed another tool package's resourceType to collide with it (postgresreplicationstats.go:30).","commonSituations":"Cloning this package as a template for a new stats tool without renaming resourceType; vendor/fork duplication; merge conflict leftovers; a custom build importing both upstream and an internal patched copy.","solutions":["Grep the repo for \"postgres-replication-stats\" and delete/rename the duplicate registration.","Assign a unique resourceType to any copied package.","Remove duplicate import paths or vendor entries and rebuild cleanly.","Re-verify with a pristine checkout to rule out local edits as the cause."],"exampleFix":"// before\nconst resourceType string = \"postgres-replication-stats\" // registered twice\n\n// after\n// stock package keeps \"postgres-replication-stats\";\n// new package:\nconst resourceType string = \"postgres-logical-replication-stats\"","handlingStrategy":"validation","validationCode":"// Static check you can run in CI before building:\n// grep -r 'postgres-replication-stats' --include='*.go' | wc -l  -> must match expected occurrences (1 const + 1 panic format only)\n// Or programmatically:\nif n := countPackagesDeclaring(\"postgres-replication-stats\"); n > 1 {\n    return fmt.Errorf(\"%q declared in %d packages\", \"postgres-replication-stats\", n)\n}","typeGuard":"func assertSingleOwner(rt string, owners []string) bool {\n    return len(owners) == 1\n}","tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        err = fmt.Errorf(\"panic during tool registration (duplicate key?): %v\", r)\n    }\n}() // place around the first server start in your launcher if registration is made lazy in a fork","preventionTips":["Never register an existing tool type string from a new package; extend the existing tool instead.","Add a precommit grep/lint step for duplicate resourceType constants.","After vendoring or forking, run `go build ./...` immediately to catch init panics.","Document tool naming rules (kebab-case, unique) in your team's contribution checklist."],"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"}