temporalio/temporal · critical

ActivityTaskScheduledEventAttributes.ActivityID is not set

Error message

ActivityTaskScheduledEventAttributes.ActivityID is not set

What it means

Immediately after verifying the scheduled event attributes are present, matching_engine.go requires ActivityId to be non-empty; an activity task without an activity ID cannot be correlated back to the workflow, so the code panics. This enforces a history-service invariant that every scheduled activity has an ID.

Source

Thrown at service/matching/matching_engine.go:3389

		NextPageToken:              resp.NextPageToken,
		PollerScalingDecision:      resp.PollerScalingDecision,
	}
	return newResp, nil
}

// Populate the activity task response based on context and scheduled/started events.
func (e *matchingEngineImpl) createPollActivityTaskQueueResponse(
	task *internalTask,
	historyResponse *historyservice.RecordActivityTaskStartedResponse,
	metricsHandler metrics.Handler,
) *matchingservice.PollActivityTaskQueueResponse {
	scheduledEvent := historyResponse.ScheduledEvent
	if scheduledEvent.GetActivityTaskScheduledEventAttributes() == nil {
		panic("GetActivityTaskScheduledEventAttributes is not set")
	}
	attributes := scheduledEvent.GetActivityTaskScheduledEventAttributes()
	if attributes.ActivityId == "" {
		panic("ActivityTaskScheduledEventAttributes.ActivityID is not set")
	}
	if task.responseC == nil {
		ct := timestamp.TimeValue(task.event.Data.CreateTime)
		metrics.AsyncMatchLatencyPerTaskQueue.With(metricsHandler).Record(time.Since(ct))
	}

	componentRef := task.event.GetData().GetComponentRef()
	activityAttemptStamp := int32(0)
	if len(componentRef) > 0 {
		activityAttemptStamp = task.event.Data.GetStamp()
	}

	taskToken := tasktoken.NewActivityTaskToken(
		task.event.Data.GetNamespaceId(),
		task.event.Data.GetWorkflowId(),
		task.event.Data.GetRunId(),
		task.event.Data.GetScheduledEventId(),
		attributes.GetActivityId(),

View on GitHub (pinned to bde624efd1)

Solutions

  1. Populate attributes.ActivityId when building the scheduled event (it must match what history generated)
  2. Verify the persisted history event actually contains an ActivityId; if empty in DB, treat as corruption and escalate
  3. Check for version skew between services where ActivityId population was added or renamed

Example fix

// before
attrs := &historypb.ActivityTaskScheduledEventAttributes{
	ActivityType: &commonpb.ActivityType{Name: "myActivity"},
}

// after
attrs := &historypb.ActivityTaskScheduledEventAttributes{
	ActivityId:   runIDGeneratedActivityID,
	ActivityType: &commonpb.ActivityType{Name: "myActivity"},
}
Defensive patterns

Strategy: validation

Validate before calling

attrs := resp.GetScheduledEvent().GetActivityTaskScheduledEventAttributes()
if attrs == nil || attrs.ActivityId == "" {
	return fmt.Errorf("activity scheduled event missing ActivityId")
}

Type guard

func activityIDSet(ev *historypb.HistoryEvent) bool {
	return ev.GetActivityTaskScheduledEventAttributes().GetActivityId() != ""
}

Try / catch

func safeConvert(resp *historyservice.RecordActivityTaskStartedResponse) (r *matchingservice.PollActivityTaskQueueResponse, err error) {
	defer func() {
		if rec := recover(); rec != nil {
			err = fmt.Errorf("activity task conversion panic: %v", rec)
		}
	}()
	return convert(resp)
}

Prevention

When it happens

Trigger: History persisted an ActivityTaskScheduledEvent with an empty ActivityId field — typically from a hand-built response in tests, a corrupted history record, or a producer bug that skipped populating ActivityId.

Common situations: Unit/integration tests constructing ActivityTaskScheduledEventAttributes manually and forgetting ActivityId; data corruption in persistence; protocol changes between versions.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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