{"record":{"id":"a86a71872280f084","repo":"temporalio/temporal","slug":"createtaskqueue-encountered-expirytime-not-set-for","errorCode":null,"errorMessage":"CreateTaskQueue encountered ExpiryTime not set for sticky task queue","messagePattern":"CreateTaskQueue encountered ExpiryTime not set for sticky task queue","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/persistence/task_manager.go","lineNumber":53,"sourceCode":"\nfunc (m *taskManagerImpl) Close() {\n\tm.taskStore.Close()\n}\n\nfunc (m *taskManagerImpl) GetName() string {\n\treturn m.taskStore.GetName()\n}\n\nfunc (m *taskManagerImpl) CreateTaskQueue(\n\tctx context.Context,\n\trequest *CreateTaskQueueRequest,\n) (*CreateTaskQueueResponse, error) {\n\ttaskQueueInfo := request.TaskQueueInfo\n\tif taskQueueInfo.LastUpdateTime == nil {\n\t\tpanic(\"CreateTaskQueue encountered LastUpdateTime not set\")\n\t}\n\tif taskQueueInfo.ExpiryTime == nil && taskQueueInfo.GetKind() == enumspb.TASK_QUEUE_KIND_STICKY {\n\t\tpanic(\"CreateTaskQueue encountered ExpiryTime not set for sticky task queue\")\n\t}\n\ttaskQueueInfoBlob, err := m.serializer.TaskQueueInfoToBlob(taskQueueInfo)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinternalRequest := &InternalCreateTaskQueueRequest{\n\t\tNamespaceID:   request.TaskQueueInfo.GetNamespaceId(),\n\t\tTaskQueue:     request.TaskQueueInfo.GetName(),\n\t\tTaskType:      request.TaskQueueInfo.GetTaskType(),\n\t\tTaskQueueKind: request.TaskQueueInfo.GetKind(),\n\t\tRangeID:       request.RangeID,\n\t\tExpiryTime:    taskQueueInfo.ExpiryTime,\n\t\tTaskQueueInfo: taskQueueInfoBlob,\n\t}\n\tif err := m.taskStore.CreateTaskQueue(ctx, internalRequest); err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/persistence/task_manager.go#L35-L71","documentation":"CreateTaskQueue in the persistence task manager panics when a sticky task queue is created without an ExpiryTime. Sticky task queues are ephemeral (they point at a specific workflow worker's cache) and the persistence layer relies on ExpiryTime to garbage-collect them; a missing ExpiryTime for TASK_QUEUE_KIND_STICKY is treated as a caller invariant violation, so the manager fails fast with panic instead of writing a row that could never expire.","triggerScenarios":"Calling PersistenceManager.CreateTaskQueue with request.TaskQueueInfo.Kind == enumspb.TASK_QUEUE_KIND_STICKY while TaskQueueInfo.ExpiryTime is nil (also requires LastUpdateTime set, otherwise the earlier LastUpdateTime panic fires first).","commonSituations":"Hand-constructing CreateTaskQueueRequest in tests or tools without setting ExpiryTime; a worker client library or matching-service code path that changed Kind to sticky after a refactor and forgot to set the expiry; version skew where an older frontend builds sticky queue info without ExpiryTime.","solutions":["Set TaskQueueInfo.ExpiryTime to a concrete timestamp (typically now + sticky task queue TTL, e.g. time.Now().Add(stickyTTL).UTC()) whenever Kind is TASK_QUEUE_KIND_STICKY.","Derive the expiry from the client's StickyTaskQueueScheduleToStartTimeout / worker sticky settings rather than leaving it nil.","If the queue is genuinely not sticky, verify Kind is left as TASK_QUEUE_KIND_NORMAL so the ExpiryTime check is bypassed.","Add a unit test asserting ExpiryTime != nil for sticky kinds before invoking the persistence layer."],"exampleFix":"// before\ntqInfo := &persistencespb.TaskQueueInfo{\n    Kind: enumspb.TASK_QUEUE_KIND_STICKY,\n    LastUpdateTime: timestamp.TimePtr(time.Now().UTC()),\n}\n// after\ntqInfo := &persistencespb.TaskQueueInfo{\n    Kind: enumspb.TASK_QUEUE_KIND_STICKY,\n    LastUpdateTime: timestamp.TimePtr(time.Now().UTC()),\n    ExpiryTime: timestamp.TimePtr(time.Now().UTC().Add(stickyTaskQueueTTL)),\n}","handlingStrategy":"validation","validationCode":"func validateStickyTQ(tq *persistencespb.TaskQueueInfo) error {\n    if tq.GetKind() == enumspb.TASK_QUEUE_KIND_STICKY && tq.ExpiryTime == nil {\n        return fmt.Errorf(\"sticky task queue %q requires ExpiryTime\", tq.GetName())\n    }\n    return nil\n}","typeGuard":"func isSticky(tq *persistencespb.TaskQueueInfo) bool { return tq.GetKind() == enumspb.TASK_QUEUE_KIND_STICKY }","tryCatchPattern":"// Go panics are not catchable as errors without recover; validate before calling:\nif err := validateStickyTQ(tqInfo); err != nil { return nil, err }\n_, err := mgr.CreateTaskQueue(ctx, req)","preventionTips":["Centralize task queue info construction in one helper that always sets ExpiryTime for sticky kind","Add table-driven tests asserting sticky requests carry ExpiryTime","Never hand-write CreateTaskQueueRequest in tools/tests without the validation helper"],"tags":["persistence","panic","task-queue","sticky"],"backgroundTag":"missing-expiry-time-for-sticky-task-queue","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}