{"record":{"id":"61a355c38cf35fc4","repo":"googleapis/mcp-toolbox","slug":"tool-type-q-already-registered-61a355","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/postgreslisttriggers/postgreslisttriggers.go","lineNumber":80,"sourceCode":"        JOIN pg_namespace n\n            ON c.relnamespace = n.oid\n        LEFT JOIN pg_proc p\n            ON t.tgfoid = p.oid\n        WHERE NOT t.tgisinternal\n    )\n    SELECT *\n    FROM trigger_list\n    WHERE\n        ($1::text IS NULL OR trigger_name LIKE '%' || $1::text || '%')\n        AND ($2::text IS NULL OR schema_name LIKE '%' || $2::text || '%')\n        AND ($3::text IS NULL OR table_name LIKE '%' || $3::text || '%')\n    ORDER BY schema_name, table_name, trigger_name\n    LIMIT COALESCE($4::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":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/postgres/postgreslisttriggers/postgreslisttriggers.go#L62-L98","documentation":"This panic comes from the package-level init() of the postgres-list-triggers tool package. tools.Register() is a global registry keyed by tool type string and returns false when the key \"postgres-list-triggers\" is already present. Go invokes init() at startup, so a duplicate registration of this key crashes the process before any configuration is loaded.","triggerScenarios":"Importing a build where two packages call tools.Register with the key \"postgres-list-triggers\": a duplicated/copied postgreslisttriggers package under a different import path, or a local edit changing some package's resourceType to collide with postgres-list-triggers (postgreslisttriggers.go:30).","commonSituations":"Copy-pasting postgreslisttriggers.go into a new tool package without changing resourceType; vendoring or forking that exposes the same package twice; merge conflicts leaving a stale duplicate init(); experimental tool packages accidentally registered under an existing key.","solutions":["Grep the repository for \"postgres-list-triggers\" and remove or rename the package/constant registering it a second time.","If the package was copied as a template, set a unique resourceType in the copy.","Deduplicate import paths (go.mod replace directives, vendor/) and rebuild.","Verify with a fresh clone that the panic is not caused by local uncommitted edits."],"exampleFix":"// before\nconst resourceType string = \"postgres-list-triggers\" // in a copied package too\n\n// after\n// original package keeps \"postgres-list-triggers\";\n// the new package uses:\nconst resourceType string = \"postgres-list-scheduled-triggers\"","handlingStrategy":"validation","validationCode":"// Before building/starting, verify the key appears in exactly one package:\n// go run: iterate all packages' resourceType constants and error on duplicates.\nconst want = \"postgres-list-triggers\"\nif registrations[want] > 1 { // registrations collected by your registry scan\n    panic(\"postgres-list-triggers registered more than once\")\n}","typeGuard":"func isRegisteredOnce(key string, registry map[string]int) bool {\n    return registry[key] == 1\n}","tryCatchPattern":"func start() (err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"startup: %v\", r)\n        }\n    }()\n    _ = tools.Register // force init of tool packages behind an explicit call in your fork\n    return nil\n}","preventionTips":["Never duplicate a tool package without renaming its resourceType first.","Keep one init()/Register call per tool type in the codebase.","Use a repo-wide grep in code review for the new tool's key to confirm uniqueness.","Prefer `go run .` smoke tests over long-running builds to catch init panics early."],"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"}