temporalio/temporal · warning

queue already exists

Error message

queue already exists

What it means

ErrQueueAlreadyExists (common/persistence/history_task_queue_manager.go:44) is returned by CreateQueue and WriteTaskToDLQ when a queue with the same identity (category + shard ID) already exists in persistence. Queue creation is expected to be idempotent-exclusive: creating a duplicate queue name is rejected rather than silently reusing it. This is often benign during concurrent startup, where several history shards race to create the same queue.

Source

Thrown at common/persistence/history_task_queue_manager.go:44

	//  Raw Task (a proto): <-- when this cannot be deserialized
	//	- ShardID
	//	- Blob (a serialized task)
	ErrMsgDeserializeRawHistoryTask = "failed to deserialize raw history task from task queue"
	// ErrMsgDeserializeHistoryTask is returned when the history task cannot be deserialized from the task queue. This
	// error is returned when the blob inside the raw task cannot be deserialized.
	//  Raw Task (a proto):
	//	- ShardID
	//	- Blob (a serialized task) <-- when this cannot be deserialized
	ErrMsgDeserializeHistoryTask = "failed to deserialize history task blob"
	// ErrMsgFailedToParseCategoryID is returned when category id cannot be parsed as an integer value.
	ErrMsgFailedToParseCategoryID = "failed to parse category id from queue name"
)

var (
	ErrReadTasksNonPositivePageSize = errors.New("page size to read history tasks must be positive")
	ErrHistoryTaskBlobIsNil         = errors.New("history task from queue has nil blob")
	ErrEnqueueTaskRequestTaskIsNil  = errors.New("enqueue task request task is nil")
	ErrQueueAlreadyExists           = errors.New("queue already exists")
	ErrShardIDInvalid               = errors.New("shard ID must be greater than 0")
	ErrInvalidQueueName             = errors.New("invalid queue name, expected 4 fields")
)

func NewHistoryTaskQueueManager(
	queue QueueV2,
	serializer serialization.Serializer,
) *HistoryTaskQueueManagerImpl {
	return &HistoryTaskQueueManagerImpl{
		queue:      queue,
		serializer: serializer,
	}
}

func (m *HistoryTaskQueueManagerImpl) EnqueueTask(
	ctx context.Context,
	request *EnqueueTaskRequest,
) (*EnqueueTaskResponse, error) {

View on GitHub (pinned to bde624efd1)

Solutions

  1. Treat errors.Is(err, ErrQueueAlreadyExists) as success in creation paths and proceed to use the existing queue
  2. Add a unique-constraint/exists check before CreateQueue, or use a create-if-not-exists API if available
  3. For WriteTaskToDLQ, ensure the DLQ is created once and reused rather than re-created per write

Example fix

// before
if err := mgr.CreateQueue(ctx, req); err != nil {
    return err // fails on restart/concurrent startup
}
// after
if err := mgr.CreateQueue(ctx, req); err != nil && !errors.Is(err, persistence.ErrQueueAlreadyExists) {
    return err
} // queue already present: reuse it
Defensive patterns

Strategy: type-guard

Type guard

func isQueueAlreadyExists(err error) bool {
    return errors.Is(err, persistence.ErrQueueAlreadyExists)
}

Try / catch

err := mgr.CreateQueue(ctx, req)
if err != nil && !errors.Is(err, persistence.ErrQueueAlreadyExists) {
    return err // real failure; duplicate creation is benign
}
// proceed using the existing queue

Prevention

When it happens

Trigger: Calling CreateQueue for a (category, shardID) pair already present in the queue persistence; WriteTaskToDLQ when the DLQ for that queue already exists and the store does not support writing to an existing DLQ implicitly.

Common situations: Concurrent history-shard startup where multiple instances race to create the same queue; re-running initialization/bootstrap code without idempotency handling; attempting to write to a DLQ that was already created by a previous run.

Related errors


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