{"record":{"id":"8f797a54a17a2017","repo":"temporalio/temporal","slug":"activitytaskscheduledeventattributes-activityid-is","errorCode":null,"errorMessage":"ActivityTaskScheduledEventAttributes.ActivityID is not set","messagePattern":"ActivityTaskScheduledEventAttributes\\.ActivityID is not set","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"service/matching/matching_engine.go","lineNumber":3389,"sourceCode":"\t\tNextPageToken:              resp.NextPageToken,\n\t\tPollerScalingDecision:      resp.PollerScalingDecision,\n\t}\n\treturn newResp, nil\n}\n\n// Populate the activity task response based on context and scheduled/started events.\nfunc (e *matchingEngineImpl) createPollActivityTaskQueueResponse(\n\ttask *internalTask,\n\thistoryResponse *historyservice.RecordActivityTaskStartedResponse,\n\tmetricsHandler metrics.Handler,\n) *matchingservice.PollActivityTaskQueueResponse {\n\tscheduledEvent := historyResponse.ScheduledEvent\n\tif scheduledEvent.GetActivityTaskScheduledEventAttributes() == nil {\n\t\tpanic(\"GetActivityTaskScheduledEventAttributes is not set\")\n\t}\n\tattributes := scheduledEvent.GetActivityTaskScheduledEventAttributes()\n\tif attributes.ActivityId == \"\" {\n\t\tpanic(\"ActivityTaskScheduledEventAttributes.ActivityID is not set\")\n\t}\n\tif task.responseC == nil {\n\t\tct := timestamp.TimeValue(task.event.Data.CreateTime)\n\t\tmetrics.AsyncMatchLatencyPerTaskQueue.With(metricsHandler).Record(time.Since(ct))\n\t}\n\n\tcomponentRef := task.event.GetData().GetComponentRef()\n\tactivityAttemptStamp := int32(0)\n\tif len(componentRef) > 0 {\n\t\tactivityAttemptStamp = task.event.Data.GetStamp()\n\t}\n\n\ttaskToken := tasktoken.NewActivityTaskToken(\n\t\ttask.event.Data.GetNamespaceId(),\n\t\ttask.event.Data.GetWorkflowId(),\n\t\ttask.event.Data.GetRunId(),\n\t\ttask.event.Data.GetScheduledEventId(),\n\t\tattributes.GetActivityId(),","sourceCodeStart":3371,"sourceCodeEnd":3407,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/service/matching/matching_engine.go#L3371-L3407","documentation":"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.","triggerScenarios":"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.","commonSituations":"Unit/integration tests constructing ActivityTaskScheduledEventAttributes manually and forgetting ActivityId; data corruption in persistence; protocol changes between versions.","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"],"exampleFix":"// before\nattrs := &historypb.ActivityTaskScheduledEventAttributes{\n\tActivityType: &commonpb.ActivityType{Name: \"myActivity\"},\n}\n\n// after\nattrs := &historypb.ActivityTaskScheduledEventAttributes{\n\tActivityId:   runIDGeneratedActivityID,\n\tActivityType: &commonpb.ActivityType{Name: \"myActivity\"},\n}","handlingStrategy":"validation","validationCode":"attrs := resp.GetScheduledEvent().GetActivityTaskScheduledEventAttributes()\nif attrs == nil || attrs.ActivityId == \"\" {\n\treturn fmt.Errorf(\"activity scheduled event missing ActivityId\")\n}","typeGuard":"func activityIDSet(ev *historypb.HistoryEvent) bool {\n\treturn ev.GetActivityTaskScheduledEventAttributes().GetActivityId() != \"\"\n}","tryCatchPattern":"func safeConvert(resp *historyservice.RecordActivityTaskStartedResponse) (r *matchingservice.PollActivityTaskQueueResponse, err error) {\n\tdefer func() {\n\t\tif rec := recover(); rec != nil {\n\t\t\terr = fmt.Errorf(\"activity task conversion panic: %v\", rec)\n\t\t}\n\t}()\n\treturn convert(resp)\n}","preventionTips":["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"],"tags":["go","panic","matching","activity-id","invariant-violation"],"backgroundTag":"missing-required-argument","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}