temporalio/temporal · error

UpdateTaskQueue encountered LastUpdateTime not set

Error message

UpdateTaskQueue encountered LastUpdateTime not set

What it means

UpdateTaskQueue in the persistence task manager panics when TaskQueueInfo.LastUpdateTime is nil. LastUpdateTime is used by the persistence layer for optimistic staleness checks and by matching/history to ignore out-of-order task queue updates, so its absence is an unrecoverable caller bug and the manager intentionally panics rather than persisting an update with no ordering information.

Source

Thrown at common/persistence/task_manager.go:81

		TaskType:      request.TaskQueueInfo.GetTaskType(),
		TaskQueueKind: request.TaskQueueInfo.GetKind(),
		RangeID:       request.RangeID,
		ExpiryTime:    taskQueueInfo.ExpiryTime,
		TaskQueueInfo: taskQueueInfoBlob,
	}
	if err := m.taskStore.CreateTaskQueue(ctx, internalRequest); err != nil {
		return nil, err
	}
	return &CreateTaskQueueResponse{}, nil
}

func (m *taskManagerImpl) UpdateTaskQueue(
	ctx context.Context,
	request *UpdateTaskQueueRequest,
) (*UpdateTaskQueueResponse, error) {
	taskQueueInfo := request.TaskQueueInfo
	if taskQueueInfo.LastUpdateTime == nil {
		panic("UpdateTaskQueue encountered LastUpdateTime not set")
	}
	if taskQueueInfo.ExpiryTime == nil && taskQueueInfo.GetKind() == enumspb.TASK_QUEUE_KIND_STICKY {
		panic("UpdateTaskQueue encountered ExpiryTime not set for sticky task queue")
	}
	taskQueueInfoBlob, err := m.serializer.TaskQueueInfoToBlob(taskQueueInfo)
	if err != nil {
		return nil, err
	}

	internalRequest := &InternalUpdateTaskQueueRequest{
		NamespaceID:   request.TaskQueueInfo.GetNamespaceId(),
		TaskQueue:     request.TaskQueueInfo.GetName(),
		TaskType:      request.TaskQueueInfo.GetTaskType(),
		RangeID:       request.RangeID,
		TaskQueueInfo: taskQueueInfoBlob,

		TaskQueueKind: request.TaskQueueInfo.GetKind(),
		ExpiryTime:    taskQueueInfo.ExpiryTime,

View on GitHub (pinned to bde624efd1)

Solutions

  1. Always set LastUpdateTime to time.Now().UTC() (wrapped with timestamp.TimePtr) immediately before building UpdateTaskQueueRequest.
  2. When carrying forward an existing TaskQueueInfo, refresh LastUpdateTime instead of reusing the stale/nil value from the loaded blob.
  3. If the info comes from deserialized storage, normalize nil LastUpdateTime to the current time after loading.
  4. Add a pre-send assertion in the calling service (matching/frontend) that LastUpdateTime is non-nil.

Example fix

// before
_, err := taskManager.UpdateTaskQueue(ctx, &persistence.UpdateTaskQueueRequest{
    TaskQueueInfo: taskQueueInfo, // LastUpdateTime nil
})
// after
taskQueueInfo.LastUpdateTime = timestamp.TimePtr(time.Now().UTC())
_, err := taskManager.UpdateTaskQueue(ctx, &persistence.UpdateTaskQueueRequest{
    TaskQueueInfo: taskQueueInfo,
})
Defensive patterns

Strategy: validation

Validate before calling

func validateTQLastUpdate(tq *persistencespb.TaskQueueInfo) error {
    if tq.LastUpdateTime == nil {
        return fmt.Errorf("task queue %q requires LastUpdateTime", tq.GetName())
    }
    return nil
}

Type guard

func hasLastUpdateTime(tq *persistencespb.TaskQueueInfo) bool { return tq.GetLastUpdateTime() != nil && !tq.GetLastUpdateTime().AsTime().IsZero() }

Try / catch

// validate before the persistence call; do not rely on recover
if err := validateTQLastUpdate(tqInfo); err != nil { return nil, err }
_, err := mgr.UpdateTaskQueue(ctx, &persistence.UpdateTaskQueueRequest{TaskQueueInfo: tqInfo})

Prevention

When it happens

Trigger: Calling PersistenceManager.UpdateTaskQueue with request.TaskQueueInfo.LastUpdateTime == nil (the sticky-kind ExpiryTime panic at line 84 fires only after this one, so a nil LastUpdateTime masks it).

Common situations: Constructing UpdateTaskQueueRequest by hand in tests or CLI tools; a new code path that copies TaskQueueInfo from an older proto message predating the LastUpdateTime field; decoding old persisted blobs that lack LastUpdateTime and reusing them for update.

Related errors


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