gastownhall/beads · error

inherit labels: parentID must not be empty

Error message

inherit labels: parentID must not be empty

What it means

A guard error thrown by the label inheritance use-case (inherit in internal/storage/domain/label.go) when the parent issue ID is an empty string. The parent's labels are the source for inheritance, so a parent ID is required. Raised before any repository access.

Source

Thrown at internal/storage/domain/label.go:234

		return nil, fmt.Errorf("get labels bulk: %w", err)
	}
	return out, nil
}

func (u *labelUseCaseImpl) InheritFromParent(ctx context.Context, childID, parentID, actor string, skipExisting []string) ([]string, error) {
	return u.inherit(ctx, childID, parentID, actor, skipExisting, false)
}

func (u *labelUseCaseImpl) InheritFromWispParent(ctx context.Context, childWispID, parentWispID, actor string, skipExisting []string) ([]string, error) {
	return u.inherit(ctx, childWispID, parentWispID, actor, skipExisting, true)
}

func (u *labelUseCaseImpl) inherit(ctx context.Context, childID, parentID, actor string, skipExisting []string, useWisp bool) ([]string, error) {
	if childID == "" {
		return nil, fmt.Errorf("inherit labels: childID must not be empty")
	}
	if parentID == "" {
		return nil, fmt.Errorf("inherit labels: parentID must not be empty")
	}
	parentLabels, err := u.labelRepo.List(ctx, parentID, LabelOpts{UseWispsTable: useWisp})
	if err != nil {
		return nil, fmt.Errorf("inherit labels: list parent %s: %w", parentID, err)
	}
	if len(parentLabels) == 0 {
		return nil, nil
	}
	skip := make(map[string]bool, len(skipExisting))
	for _, s := range skipExisting {
		skip[s] = true
	}
	var inherited []string
	for _, label := range parentLabels {
		if skip[label] {
			continue
		}
		if err := u.labelRepo.Insert(ctx, childID, label, actor, LabelOpts{UseWispsTable: useWisp}); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Resolve and verify the parent issue ID before calling inheritance (e.g., from the issue's dependency or parent field).
  2. If the issue genuinely has no parent, skip inheritance for it instead of calling with an empty ID.
  3. Validate parentID non-empty at the call site and return a clearer domain error there.

Example fix

// before
var parentID string // never assigned
uc.InheritFromParent(ctx, childID, parentID, actor, nil)
// after
if parentID == "" { return nil } // no parent: nothing to inherit
uc.InheritFromParent(ctx, childID, parentID, actor, nil)
Defensive patterns

Strategy: validation

Validate before calling

if parentID == "" { return nil } // no parent: skip inheritance

Type guard

func hasParentID(parentID string) bool { return strings.TrimSpace(parentID) != "" }

Prevention

When it happens

Trigger: Calling InheritFromParent(ctx, childID, "", actor, skipExisting) or InheritFromWispParent(ctx, childWispID, "", actor, skipExisting) — the parent reference was never resolved or an empty parent struct field was passed.

Common situations: A dependency lookup returned no parent (e.g., issue has no 'blocks/blocked-by' or parent relationship) and the code passed the zero-value ID; JSON configs missing the parent field; wisps whose parent pointer was never set.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/fb4baa3c482a90a7. Report an issue: GitHub.