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
- Treat as expected: check errors.Is(err, persistence.ErrQueueAlreadyExists) and proceed as if the queue exists (idempotent create).
- Use a different queue name if a new distinct queue was intended.
- 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
- Make queue creation idempotent by handling ErrQueueAlreadyExists as success.
- Use deterministic queue names so repeated deploys don't collide.
- Serialize queue-creation calls for the same name through a single owner.
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
- rowsAffected returned error when initializing DLQ metadata
- %w: id is %d but must be >= %d
- queue with type %v and name %v has invalid encoding: %w
- unmarshal payload for queue with type %v and name %v failed:
- rowsAffected returned error for shardID %v: %v
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/81c5c677cb50ddba.
Report an issue: GitHub.