temporalio/temporal · critical

category id: %v has already been defined as type %v and name

Error message

category id: %v has already been defined as type %v and name %v

What it means

MutableTaskCategoryRegistry.AddCategory registers a task Category keyed by its numeric ID. It deliberately panics (instead of erroring) when the same ID is registered twice, since category registration happens during package init and a duplicate indicates a compile-time-level configuration bug. The existing registration's type and name are reported to help identify the collision.

Source

Thrown at service/history/tasks/task_category_registry.go:45

// you're in a test.
func NewDefaultTaskCategoryRegistry() *MutableTaskCategoryRegistry {
	return &MutableTaskCategoryRegistry{
		categories: map[int]Category{
			CategoryTransfer.ID():    CategoryTransfer,
			CategoryTimer.ID():       CategoryTimer,
			CategoryVisibility.ID():  CategoryVisibility,
			CategoryReplication.ID(): CategoryReplication,
			CategoryMemoryTimer.ID(): CategoryMemoryTimer,
			CategoryOutbound.ID():    CategoryOutbound,
		},
	}
}

// AddCategory register a Category with the registry or panics if a Category with the same ID has already been
// registered.
func (r *MutableTaskCategoryRegistry) AddCategory(c Category) {
	if category, ok := r.categories[c.id]; ok {
		panic(fmt.Sprintf(
			"category id: %v has already been defined as type %v and name %v",
			c.id,
			category.cType,
			category.name,
		))
	}

	r.categories[c.id] = c
}

// GetCategoryByID returns a registered Category with the same ID from the registry or false if no such Category exists.
func (r *MutableTaskCategoryRegistry) GetCategoryByID(id int) (Category, bool) {
	category, ok := r.categories[id]
	return category, ok
}

// GetCategories returns a deep copy of all registered Category objects from the registry.
func (r *MutableTaskCategoryRegistry) GetCategories() map[int]Category {

View on GitHub (pinned to bde624efd1)

Solutions

  1. Allocate a new, unused category ID for the new category; check all RegisterTaskCategory calls in the tasks package for taken IDs.
  2. Remove the duplicate AddCategory call (often left by a bad merge).
  3. Search for all categories registered with the reported id and decide which definition is authoritative.
  4. Coordinate ID allocation across teams/forks since IDs are persisted in task rows and must be globally unique.

Example fix

// before
CategoryTransfer = 1
CategoryReplication = 1 // collides

// after
CategoryTransfer = 1
CategoryReplication = 2
Defensive patterns

Strategy: validation

Validate before calling

if registry.GetCategory(2) != nil {
    return errors.New("category id 2 already registered")
}
registry.AddCategory(myCategory)

Prevention

When it happens

Trigger: Calling AddCategory with a Category whose id collides with one already registered — happens when defining a new task category reusing an existing numeric ID, or registering the same category twice (e.g. duplicated init code).

Common situations: Adding a new category in a fork/patch without allocating a fresh ID; merge conflicts leaving two registration blocks; upstream ID reassignment causing collisions with locally added categories.

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/6daefc8fc0ad49bb. Report an issue: GitHub.