{"record":{"id":"0e7a50554afa44ed","repo":"googleapis/mcp-toolbox","slug":"tool-type-q-already-registered-0e7a50","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/neo4j/neo4jcypher/neo4jcypher.go","lineNumber":34,"sourceCode":"\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/goccy/go-yaml\"\n\n\t\"github.com/googleapis/mcp-toolbox/internal/sources\"\n\t\"github.com/googleapis/mcp-toolbox/internal/tools\"\n\t\"github.com/googleapis/mcp-toolbox/internal/util\"\n\t\"github.com/googleapis/mcp-toolbox/internal/util/parameters\"\n)\n\nconst resourceType string = \"neo4j-cypher\"\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\tNeo4jDatabase() string // kept to ensure neo4j source\n\tRunQuery(context.Context, string, map[string]any, bool, bool) (any, error)\n}\n\ntype Config struct {\n\ttools.ConfigBase `yaml:\",inline\"`","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/neo4j/neo4jcypher/neo4jcypher.go#L16-L52","documentation":"This is a fail-fast panic raised during package initialization. The neo4jcypher package registers its factory under resourceType \"neo4j-cypher\" in the global toolRegistry via tools.Register() (internal/tools/tools.go:43). A false return from Register() means the type string is already in the registry map, so init() panics rather than allowing a silent overwrite that would make YAML configs with `kind: neo4j-cypher` decode to the wrong tool.","triggerScenarios":"Two init() functions register \"neo4j-cypher\": a duplicated/renamed copy of this package still using the constant, the package linked under two import paths (module replace/fork), or a new tool reusing the built-in type string.","commonSituations":"Copying neo4jcypher as a starting point for a variant Cypher tool and forgetting the const; a merge reintroducing an old package import; vendoring two versions of the toolbox module.","solutions":["grep -rn '\"neo4j-cypher\"' to locate both registrations; give the duplicate a unique resourceType.","Remove the stale import or delete the obsolete package copy.","Deduplicate module copies (go mod tidy, fix replace directives).","Verify with `go build ./...` and `go test ./internal/tools/neo4j/...`."],"exampleFix":"// before\nconst resourceType string = \"neo4j-cypher\" // in both original and copied package\n\n// after\nconst resourceType string = \"neo4j-cypher\" // original only; new tool uses \"neo4j-read-cypher\"","handlingStrategy":"validation","validationCode":"// Verify uniqueness before building:\n// grep -rn '\"neo4j-cypher\"' --include='*.go' . | grep 'resourceType\\|tools.Register'\n// Exactly one match expected. Soft guard for custom tools:\nfunc init() {\n\tif !tools.Register(resourceType, newConfig) {\n\t\tfmt.Printf(\"WARNING: %s already registered; skipping duplicate init\\n\", resourceType)\n\t\treturn\n\t}\n}","typeGuard":"func isRegistered(resourceType string) bool {\n\t_, exists := registrySnapshot[resourceType]\n\treturn exists\n}","tryCatchPattern":"// Recover at the process boundary:\nfunc main() {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tif s, ok := r.(string); ok && strings.Contains(s, \"already registered\") {\n\t\t\t\tlog.Fatalf(\"duplicate tool registration: %s\", s)\n\t\t\t}\n\t\t\tpanic(r)\n\t\t}\n\t}()\n\trootCmd.Execute()\n}","preventionTips":["Rename resourceType, package, and directory together when templating a new Neo4j tool.","Add a CI test asserting every resourceType string is registered at most once.","Remove obsolete Neo4j tool packages instead of keeping unimported copies in the tree.","Run go mod tidy after dependency changes to avoid dual module copies.","grep the type string before committing any change to tool registration files."],"tags":["go","init-panic","tool-registry","duplicate-registration","neo4j"],"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"}