temporalio/temporal · error
unknown transfer task
Error message
unknown transfer task
What it means
GetTransferTaskEventID maps a concrete transfer Task implementation to the workflow event ID that spawned it. The type switch covers all known transfer task types; when a new transfer task implementation is added to the tasks package but not added to this switch, the default branch panics with an internal service error. Tags() relies on this function to attach event-ID tags to task metrics/logs.
Source
Thrown at service/history/tasks/utils.go:84
eventID = task.ScheduledEventID
case *CloseExecutionTask:
eventID = common.FirstEventID
case *DeleteExecutionTask:
return getChasmTaskEventID()
case *CancelExecutionTask:
eventID = task.InitiatedEventID
case *SignalExecutionTask:
eventID = task.InitiatedEventID
case *StartChildExecutionTask:
eventID = task.InitiatedEventID
case *ResetWorkflowTask:
eventID = common.FirstEventID
case *ChasmTask:
return getChasmTaskEventID()
case *FakeTask:
// no-op
default:
panic(serviceerror.NewInternal("unknown transfer task"))
}
return eventID, true
}
func GetTimerTaskEventID(
timerTask Task,
) (int64, bool) {
eventID := int64(0)
switch task := timerTask.(type) {
case *UserTimerTask:
eventID = task.EventID
case *ActivityTimeoutTask:
eventID = task.EventID
case *WorkflowTaskTimeoutTask:
eventID = task.EventID
case *WorkflowBackoffTimerTask:
eventID = common.FirstEventIDView on GitHub (pinned to bde624efd1)
Solutions
- Add a case for the unhandled task type in GetTransferTaskEventID returning the correct event ID.
- Align service versions so all history nodes know the same set of transfer task types.
- Check whether the task was corrupted in persistence (wrong type field) and repair or drop the row.
- Add a compile-time exhaustiveness aid (e.g. a unit test enumerating all transfer task types) to catch future additions.
Example fix
// before
case *ChasmTask:
return getChasmTaskEventID()
default:
panic(serviceerror.NewInternal("unknown transfer task"))
// after
case *ChasmTask:
return getChasmTaskEventID()
case *NewTransferTask:
eventID = task.GetEventID()
return eventID, true
default:
panic(serviceerror.NewInternal("unknown transfer task")) Defensive patterns
Strategy: type-guard
Validate before calling
switch t.(type) {
case *tasks.TransferWorkflowTask, *tasks.TransferActivityTask, *tasks.TransferChasmTask:
// safe to call
default:
return fmt.Errorf("transfer task type %T unsupported", t)
} Type guard
func isKnownTransferTask(t tasks.Task) bool {
switch t.(type) {
case *tasks.TransferWorkflowTask, *tasks.TransferActivityTask,
*tasks.TransferWorkflowTaskTimer, *tasks.TransferActivityTimeoutTask,
*tasks.TransferCloseTask, *tasks.TransferCancelTask, *tasks.TransferSignalTask,
*tasks.TransferDeleteExecutionTask, *tasks.TransferChasmTask, *tasks.FakeTask:
return true
}
return false
} Prevention
- Update GetTransferTaskEventID whenever a new transfer task struct is added
- Keep all history nodes on the same release version
- Add an exhaustiveness unit test enumerating all transfer task types
- Validate task type fields read from persistence
When it happens
Trigger: Passing an unhandled transfer Task concrete type (anything not covered by the switch, e.g. a new *SomeNewTask struct) into GetTransferTaskEventID — typically via Tags() on a task whose type was added upstream but not handled in this fork/version.
Common situations: Version skew: a newer Temporal release introduces a new transfer task type while an older node processes it; custom forks adding task types without updating utils.go; misdeserialized tasks yielding an unexpected concrete type.
Related errors
- unknown timer task
- unknown task predicate type: %T
- unsupported deduplication key
- unknown number type: %v
- Found key with non-zero pending task count but has no corres
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/881022a65f72def5.
Report an issue: GitHub.