temporalio/temporal · warning

root task queue partition has no parent

Error message

root task queue partition has no parent

What it means

ErrNoParent is the sentinel error returned when Parent() is called on the root partition of a task-queue family. The root partition (partition 0 of the root task queue) sits at the top of the tqid partition tree, so it has no parent by construction. Test helpers and user-data lookup use it to detect when upward traversal must stop or is invalid.

Source

Thrown at common/tqid/task_queue_id.go:123

	}

	// PartitionKey uniquely identifies a task queue partition, to be used in maps.
	// Note that task queue kind (sticky vs normal) and normal name for sticky task queues are not
	// part of the task queue partition identity.
	PartitionKey struct {
		namespaceId string
		name        string
		partitionId int
		taskType    enumspb.TaskQueueType
	}
)

var _ Partition = (*NormalPartition)(nil)
var _ Partition = (*StickyPartition)(nil)
var _ Partition = (*WorkerCommandsPartition)(nil)

var (
	ErrNoParent      = errors.New("root task queue partition has no parent")
	ErrInvalidDegree = errors.New("invalid task queue partition branching degree")
	ErrNonZeroSticky = errors.New("only sticky partitions can not have non-zero partition ID")
)

// NewTaskQueueFamily takes a user-provided task queue name (aka family name) and returns a TaskQueueFamily. Returns an
// error if name looks like a mangled name.
func NewTaskQueueFamily(namespaceId string, name string) (*TaskQueueFamily, error) {
	if strings.HasPrefix(name, nonRootPartitionPrefix) {
		return nil, serviceerror.NewInvalidArgument("task queue family name cannot have prefix /_sys/ " + name)
	}
	return &TaskQueueFamily{
		namespaceId: namespaceId,
		name:        name,
	}, nil
}

// UnsafeTaskQueueFamily returns a TaskQueueFamily object without validating the task queue name.
// This method should only be used in logs/metrics, not in the server logic (use NewTaskQueueFamily instead).

View on GitHub (pinned to bde624efd1)

Solutions

  1. Stop traversal when Parent() returns ErrNoParent — treat it as the root sentinel via errors.Is(err, tqid.ErrNoParent)
  2. Check partition ID before calling Parent(): skip the call when the partition is the root (partition ID 0 of the root task queue)
  3. Fix partition math so routing only forwards when a parent partition actually exists

Example fix

// before
parent, err := partition.Parent()
require.NoError(t, err)
// after
parent, err := partition.Parent()
if errors.Is(err, tqid.ErrNoParent) {
    return // already at root
}
require.NoError(t, err)
Defensive patterns

Strategy: try-catch

Validate before calling

if partition.ID() == 0 && partition.TaskQueue().Parent() == nil /* root family */ {
    // skip Parent() call
}

Type guard

func hasParent(p tqid.Partition) bool {
    _, err := p.Parent()
    return !errors.Is(err, tqid.ErrNoParent)
}

Try / catch

parent, err := partition.Parent()
if errors.Is(err, tqid.ErrNoParent) {
    return // root reached; stop upward traversal
}
if err != nil {
    return err
}

Prevention

When it happens

Trigger: Calling Parent() on the root NormalPartition of a TaskQueueFamily; userDataFetchSource walking to parents past the root; tests like TestForwardTaskError/TestForwardQueryTaskError/TestForwardPollError exercising forwarding beyond the root partition.

Common situations: Partition-index arithmetic that computes partition 0 (root) and still requests a parent; recursive algorithms lacking a root stop condition; forwarding logic trying to route a task/query to a nonexistent parent queue.

Related errors


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