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
- Populate attributes.ActivityId when building the scheduled event (it must match what history generated)
- Verify the persisted history event actually contains an ActivityId; if empty in DB, treat as corruption and escalate
- 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
- Always populate ActivityId when constructing ActivityTaskScheduledEventAttributes in tests or mocks
- Lint/test fixtures to require non-empty ActivityId on ACTIVITY_TASK_SCHEDULED events
- Treat empty ActivityId in persisted history as corruption and alert rather than forwarding
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
- expect at least one reservation
- Found key with non-zero pending task count but has no corres
- unknown workflow update abort reason %s or update state %s
- GetActivityTaskScheduledEventAttributes is not set
- Current cluster name is empty
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/8f797a54a17a2017.
Report an issue: GitHub.