temporalio/temporal · warning

ErrQueueAlreadyExists

ErrQueueAlreadyExists

Error message

%w: queue type %v and name %v

What it means

CreateQueue in the queue V2 store detects a duplicate-entry error from the database when inserting queue V2 metadata, and wraps it with persistence.ErrQueueAlreadyExists. This is an expected, typed conflict: the caller is trying to create a queue whose (queue type, queue name) pair already exists in the cluster.

Source

Thrown at common/persistence/sql/queue_v2.go:204

	request *persistence.InternalCreateQueueRequest,
) (*persistence.InternalCreateQueueResponse, error) {
	payload := persistencespb.Queue{
		Partitions: map[int32]*persistencespb.QueuePartition{
			defaultPartition: {
				MinMessageId: persistence.FirstQueueMessageID,
			},
		},
	}
	bytes, _ := payload.Marshal()
	row := sqlplugin.QueueV2MetadataRow{
		QueueType:        request.QueueType,
		QueueName:        request.QueueName,
		MetadataPayload:  bytes,
		MetadataEncoding: enumspb.ENCODING_TYPE_PROTO3.String(),
	}
	_, err := q.DB.InsertIntoQueueV2Metadata(ctx, &row)
	if q.DB.IsDupEntryError(err) {
		return nil, fmt.Errorf(
			"%w: queue type %v and name %v",
			persistence.ErrQueueAlreadyExists,
			request.QueueType,
			request.QueueName,
		)
	}
	if err != nil {
		return nil, serviceerror.NewUnavailablef(
			"CreateQueue failed for queue with type: %v and name: %v. InsertIntoQueueV2Metadata operation failed. Error: %v",
			request.QueueType,
			request.QueueName,
			err,
		)
	}
	return &persistence.InternalCreateQueueResponse{}, nil
}

func (q *queueV2) RangeDeleteMessages(

View on GitHub (pinned to bde624efd1)

Solutions

  1. Treat as expected: check errors.Is(err, persistence.ErrQueueAlreadyExists) and proceed as if the queue exists (idempotent create).
  2. Use a different queue name if a new distinct queue was intended.
  3. Delete the existing queue first if recreation is intended (e.g., DeleteQueue), then create again.

Example fix

// before
_, err := store.CreateQueue(ctx, req)
if err != nil { return err }
// after
_, err := store.CreateQueue(ctx, req)
if errors.Is(err, persistence.ErrQueueAlreadyExists) {
    return nil // already created; idempotent
}
if err != nil { return err }
Defensive patterns

Strategy: type-guard

Validate before calling

// pre-check if queue already exists via GetQueueMetadata
_, err := store.GetQueueMetadata(ctx, &persistence.InternalGetQueueMetadataRequest{
    QueueType: req.QueueType, QueueName: req.QueueName,
})
exists := err == nil

Type guard

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

Try / catch

_, err := store.CreateQueue(ctx, req)
if isQueueAlreadyExists(err) {
    return nil // treat as success for idempotent creation
}
return err

Prevention

When it happens

Trigger: Calling CreateQueue with a QueueType/QueueName combination that already has a metadata row; the DB insert returns a duplicate-key error matched by q.DB.IsDupEntryError.

Common situations: Re-running an idempotent-but-non-idempotent queue creation path; two services racing to create the same named queue; retrying CreateQueue after a timeout where the first attempt actually committed.

Related errors


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