temporalio/temporal · error

component type %s must be and interface or struct that imple

Error message

component type %s must be and interface or struct that implements Component interface

What it means

registerTask validates that the task's associated component type is either an interface, or a struct/pointer-to-struct that is AssignableTo the Component interface. A task must declare which component it operates on; if the declared type doesn't satisfy Component, the task cannot be dispatched. This is typically a wrong type parameter in RegisterTask.

Source

Thrown at chasm/registry.go:325

		return fmt.Errorf("task %s is already registered", fqn)
	}

	if existingTask, ok := r.rtByID[id]; ok {
		return fmt.Errorf("task type ID %d collision between %s and %s", id, fqn, existingTask.fqType())
	}

	if !(rt.goType.Kind() == reflect.Struct ||
		(rt.goType.Kind() == reflect.Pointer && rt.goType.Elem().Kind() == reflect.Struct)) {
		return fmt.Errorf("task type %s must be struct or pointer to struct", rt.goType.String())
	}
	if _, ok := r.rtByGoType[rt.goType]; ok {
		return fmt.Errorf("task type %s is already registered", rt.goType.String())
	}
	if !(rt.componentGoType.Kind() == reflect.Interface ||
		(rt.componentGoType.Kind() == reflect.Struct ||
			(rt.componentGoType.Kind() == reflect.Pointer && rt.componentGoType.Elem().Kind() == reflect.Struct)) &&
			rt.componentGoType.AssignableTo(reflect.TypeFor[Component]())) {
		return fmt.Errorf("component type %s must be and interface or struct that implements Component interface", rt.componentGoType.String())
	}

	r.rtByFqn[fqn] = rt
	r.rtByID[id] = rt
	r.rtByGoType[rt.goType] = rt
	return nil
}

func (r *Registry) validateName(n string) error {
	if n == "" {
		return errors.New("name must not be empty")
	}
	if !nameValidator.MatchString(n) {
		return fmt.Errorf("name %s is invalid. name must follow golang identifier rules: %s", n, nameValidator.String())
	}
	return nil
}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Make the component type parameter a type that implements chasm.Component (verify required methods exist).
  2. Swap type parameters if Task and Component were reversed at the call site.
  3. Add a compile-time assertion `var _ chasm.Component = (*MyComponent)(nil)` to catch drift early.

Example fix

// before
var _ chasm.Component = (*OrderDTO)(nil) // fails
chasm.RegisterTask[OrderTask, OrderDTO](reg, "order_task", opts)
// after
var _ chasm.Component = (*OrderImpl)(nil)
chasm.RegisterTask[OrderTask, OrderImpl](reg, "order_task", opts)
Defensive patterns

Strategy: type-guard

Validate before calling

var _ chasm.Component = (*MyComponent)(nil) // compile-time guard before registration

Type guard

func implementsComponent(t reflect.Type) bool {
	return t != nil && t.AssignableTo(reflect.TypeFor[chasm.Component]())
}

Prevention

When it happens

Trigger: Passing a component type parameter that does not implement chasm.Component (missing required methods, or a totally unrelated struct/pointer type) to chasm.RegisterTask.

Common situations: Component interface evolved and the type no longer satisfies it after an upgrade; passing a DTO struct instead of the component; mixing up type-parameter order in RegisterTask[Task, Component].

Related errors


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