{"record":{"id":"577a3fe83fb397eb","repo":"temporalio/temporal","slug":"task-type-s-must-be-struct-or-pointer-to-struct","errorCode":null,"errorMessage":"task type %s must be struct or pointer to struct","messagePattern":"task type (.+?) must be struct or pointer to struct","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"chasm/registry.go","lineNumber":316,"sourceCode":"\t\treturn err\n\t}\n\n\tfqn, id, err := rt.registerToLibrary(lib)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif _, ok := r.rtByFqn[fqn]; ok {\n\t\treturn fmt.Errorf(\"task %s is already registered\", fqn)\n\t}\n\n\tif existingTask, ok := r.rtByID[id]; ok {\n\t\treturn fmt.Errorf(\"task type ID %d collision between %s and %s\", id, fqn, existingTask.fqType())\n\t}\n\n\tif !(rt.goType.Kind() == reflect.Struct ||\n\t\t(rt.goType.Kind() == reflect.Pointer && rt.goType.Elem().Kind() == reflect.Struct)) {\n\t\treturn fmt.Errorf(\"task type %s must be struct or pointer to struct\", rt.goType.String())\n\t}\n\tif _, ok := r.rtByGoType[rt.goType]; ok {\n\t\treturn fmt.Errorf(\"task type %s is already registered\", rt.goType.String())\n\t}\n\tif !(rt.componentGoType.Kind() == reflect.Interface ||\n\t\t(rt.componentGoType.Kind() == reflect.Struct ||\n\t\t\t(rt.componentGoType.Kind() == reflect.Pointer && rt.componentGoType.Elem().Kind() == reflect.Struct)) &&\n\t\t\trt.componentGoType.AssignableTo(reflect.TypeFor[Component]())) {\n\t\treturn fmt.Errorf(\"component type %s must be and interface or struct that implements Component interface\", rt.componentGoType.String())\n\t}\n\n\tr.rtByFqn[fqn] = rt\n\tr.rtByID[id] = rt\n\tr.rtByGoType[rt.goType] = rt\n\treturn nil\n}\n\nfunc (r *Registry) validateName(n string) error {","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/chasm/registry.go#L298-L334","documentation":"registerTask rejects task implementations whose reflect.Type is not a struct or pointer to a struct. Like components, tasks must be backed by concrete structs; registering an interface or other kind is unsupported and usually means the interface type was passed by mistake.","triggerScenarios":"Calling chasm.RegisterTask with the task interface type as the type parameter, or with a non-struct kind (map, slice, func).","commonSituations":"Passing the interface name instead of the implementation struct; generic wrapper that accidentally instantiates with an interface; refactoring a struct into an interface.","solutions":["Use the concrete task struct (or *struct) as the type parameter.","Verify with reflect.TypeOf(task).Kind() before registering.","If generics got in the way, constrain the type parameter to struct types at the call site."],"exampleFix":"// before\nchasm.RegisterTask[TaskInterface, OrderImpl](reg, \"x\", opts)\n// after\nchasm.RegisterTask[TaskImpl, OrderImpl](reg, \"x\", opts)","handlingStrategy":"validation","validationCode":"t := reflect.TypeOf((*MyTask)(nil)).Elem()\nif !(t.Kind() == reflect.Struct || (t.Kind() == reflect.Pointer && t.Elem().Kind() == reflect.Struct)) {\n\treturn fmt.Errorf(\"task type %s must be struct or pointer to struct\", t)\n}","typeGuard":"func isTaskStruct(t reflect.Type) bool {\n\treturn t != nil && (t.Kind() == reflect.Struct || (t.Kind() == reflect.Pointer && t.Elem().Kind() == reflect.Struct))\n}","tryCatchPattern":null,"preventionTips":["Pass the concrete task struct, not its interface, to chasm.RegisterTask.","Constrain generic helpers with [T any, PT interface{ *T; chasm.Task }] style bounds.","Add a compile-time assertion for the task type where feasible."],"tags":["chasm","registry","go-reflect","task-registration"],"backgroundTag":"invalid-registered-type-kind","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}