googleapis/mcp-toolbox · critical

tool type %q already registered

Error message

tool type %q already registered

What it means

This panic comes from init() in internal/tools/databaseinsights/getadvancedtimeseriesquerystats when tools.Register(resourceType, newConfig) reports the key "databaseinsights-get-advanced-time-series-query-stats" is already present in the global tool registry. The registry intentionally rejects duplicate tool type keys by returning false, and init() converts that into a panic so the process fails at startup rather than silently replacing an existing tool. Any process importing the package twice under duplicate keys cannot start.

Source

Thrown at internal/tools/databaseinsights/getadvancedtimeseriesquerystats/getadvancedtimeseriesquerystats.go:34

import (
	"context"
	"fmt"
	"net/http"

	yaml "github.com/goccy/go-yaml"
	"github.com/googleapis/mcp-toolbox/internal/sources"
	"github.com/googleapis/mcp-toolbox/internal/sources/databaseinsights"
	"github.com/googleapis/mcp-toolbox/internal/tools"
	"github.com/googleapis/mcp-toolbox/internal/util"
	"github.com/googleapis/mcp-toolbox/internal/util/parameters"
)

const resourceType string = "databaseinsights-get-advanced-time-series-query-stats"

func init() {
	if !tools.Register(resourceType, newConfig) {
		panic(fmt.Sprintf("tool type %q already registered", resourceType))
	}
}

func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
	actual := Config{ConfigBase: tools.ConfigBase{Name: name}}
	if err := decoder.DecodeContext(ctx, &actual); err != nil {
		return nil, err
	}
	return actual, nil
}

type compatibleSource interface {
	FetchQueryTimeSeries(ctx context.Context, req *databaseinsights.FetchQueryTimeSeriesRequest) (*databaseinsights.FetchQueryTimeSeriesResponse, error)
}

type Config struct {
	tools.ConfigBase `yaml:",inline"`
	Type             string                 `yaml:"type" validate:"required"`

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Search the codebase for the string "databaseinsights-get-advanced-time-series-query-stats" and delete or rename the duplicate const.
  2. Give each tool package a distinct resourceType following the <source>-<tool> naming convention.
  3. Verify with `go vet ./...` and a clean build that only one declaration remains.
  4. In tests, never call tools.Register for a type that package init already registered; rely on init().

Example fix

// before
const resourceType string = "databaseinsights-get-advanced-time-series-query-stats"
// after
const resourceType string = "databaseinsights-get-time-series-query-stats" // unique key
Defensive patterns

Strategy: validation

Validate before calling

const want = "databaseinsights-get-advanced-time-series-query-stats"
// pre-build check: go run ./cmd/checkdup or a repo grep in CI
if err := exec.Command("git", "grep", "-n", want, "--", "*.go").Run(); err == nil {
    // review every hit; more than one const declaration is a bug
    _ = err
}

Try / catch

func safeInit() {
    defer func() {
        if r := recover(); r != nil {
            log.Fatalf("duplicate tool type registration: %v", r)
        }
    }()
    if !tools.Register(resourceType, newConfig) {
        return // or fail with a controlled error
    }
}

Prevention

When it happens

Trigger: A second package or file declares the identical resourceType "databaseinsights-get-advanced-time-series-query-stats" and both init() functions run; or a test re-executes registration logic manually after package init already registered it.

Common situations: Copy-paste of a sibling databaseinsights tool with the resourceType left unchanged; a merge conflict resolution that kept two identical consts; manually calling newConfig/Register helpers in tests that already ran via init.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/c7ec42813cd6aca9. Report an issue: GitHub.