temporalio/temporal · error

unknown timer task

Error message

unknown timer task

What it means

GetTimerTaskEventID is the timer-task analogue of GetTransferTaskEventID: it maps timer Task implementations (UserTimers, ActivityTimers, WorkflowTaskTimers, WorkflowTimeoutTimers, ActivityTimeoutTimers, ChasmTasks, etc.) to their originating event ID. An unhandled timer task concrete type falls into the default branch and panics with an internal error.

Source

Thrown at service/history/tasks/utils.go:126

		eventID = common.FirstEventID
	case *DeleteHistoryEventTask:
		// Retention task will be used by chasm framework as well,
		// and it doesn't depend on any particular state (thus eventID) of the run.
		return getChasmTaskEventID()
	case *StateMachineTimerTask:
		eventID = common.FirstEventID
	case *TimeSkippingTimerTask:
		// time skipping timer tasks supports both for workflow and chasm executions,
		// and it doesn't depend on eventID, so it uses the dummy chasm task eventID here.
		return getChasmTaskEventID()
	case *ChasmTaskPure:
		return getChasmTaskEventID()
	case *ChasmTask:
		return getChasmTaskEventID()
	case *FakeTask:
		// no-op
	default:
		panic(serviceerror.NewInternal("unknown timer task"))
	}
	return eventID, true
}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Add a case for the new timer task type returning its event ID (or a sentinel like getChasmTaskEventID equivalent).
  2. Upgrade all history nodes to a version that recognizes the task type (fix version skew).
  3. Inspect the task row in persistence to confirm the type is legitimate and not corrupt.
  4. Add an exhaustiveness unit test covering every timer task implementation in the tasks package.

Example fix

// before
case *ChasmTask:
    return getChasmTaskEventID()
default:
    panic(serviceerror.NewInternal("unknown timer task"))

// after
case *ChasmTask:
    return getChasmTaskEventID()
case *NewTimerTask:
    eventID = task.GetTaskID() // or appropriate event source
default:
    panic(serviceerror.NewInternal("unknown timer task"))
Defensive patterns

Strategy: type-guard

Validate before calling

switch t.(type) {
case *tasks.UserTimerTask, *tasks.ActivityTimeoutTask, *tasks.WorkflowTaskTimeoutTask, *tasks.ChasmTask:
    // safe to call
default:
    return fmt.Errorf("timer task type %T unsupported", t)
}

Type guard

func isKnownTimerTask(t tasks.Task) bool {
    switch t.(type) {
    case *tasks.UserTimerTask, *tasks.ActivityTimeoutTask,
        *tasks.WorkflowTaskTimeoutTask, *tasks.WorkflowTimeoutTask,
        *tasks.ActivityTimeoutTask, *tasks.ChasmTask, *tasks.FakeTask:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: Passing a timer Task whose concrete type is missing from the type switch into GetTimerTaskEventID — via Tags() when processing timer tasks, usually after a new timer task type was introduced in one version and processed by another.

Common situations: Upgrades where a new timer task type exists in persistence and is loaded by an older binary; fork-specific timer tasks; nil or wrongly-typed tasks placed in the timer queue.

Related errors


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