gastownhall/beads · error

remove label: id must not be empty

Error message

remove label: id must not be empty

What it means

A guard error from the label use case's remove(): it rejects removing a label when the target issue/wisp ID is empty. Called by RemoveLabel and RemoveWispLabel before any repository delete occurs.

Source

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

		return fmt.Errorf("add label: label must not be empty")
	}
	if err := u.labelRepo.Insert(ctx, id, label, actor, LabelOpts{UseWispsTable: useWisp}); err != nil {
		return fmt.Errorf("add label %s/%s: %w", id, label, err)
	}
	return nil
}

func (u *labelUseCaseImpl) RemoveLabel(ctx context.Context, issueID, label, actor string) error {
	return u.remove(ctx, issueID, label, actor, false)
}

func (u *labelUseCaseImpl) RemoveWispLabel(ctx context.Context, wispID, label, actor string) error {
	return u.remove(ctx, wispID, label, actor, true)
}

func (u *labelUseCaseImpl) remove(ctx context.Context, id, label, actor string, useWisp bool) error {
	if id == "" {
		return fmt.Errorf("remove label: id must not be empty")
	}
	if label == "" {
		return fmt.Errorf("remove label: label must not be empty")
	}
	if err := u.labelRepo.Delete(ctx, id, label, actor, LabelOpts{UseWispsTable: useWisp}); err != nil {
		return fmt.Errorf("remove label %s/%s: %w", id, label, err)
	}
	return nil
}

func (u *labelUseCaseImpl) AddLabels(ctx context.Context, issueID string, labels []string, actor string) error {
	return u.addMany(ctx, issueID, labels, actor, false)
}

func (u *labelUseCaseImpl) AddWispLabels(ctx context.Context, wispID string, labels []string, actor string) error {
	return u.addMany(ctx, wispID, labels, actor, true)
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Pass a resolved, non-empty issue ID to RemoveLabel/RemoveWispLabel.
  2. Fix the upstream lookup that returned an empty ID (check errors from the fetch, not just the value).
  3. In batch scripts, skip entries with empty IDs instead of calling remove for them.
  4. Validate input at the CLI/tool boundary before invoking the use case.

Example fix

// before
labelUC.RemoveLabel(ctx, os.Args[1], "stale", actor) // may be empty

// after
if len(os.Args) < 2 || os.Args[1] == "" {
    return fmt.Errorf("usage: tool <issue-id> <label>")
}
labelUC.RemoveLabel(ctx, os.Args[1], "stale", actor)
Defensive patterns

Strategy: validation

Validate before calling

func removeLabelSafe(ctx context.Context, uc LabelUseCase, id, label, actor string) error {
    if strings.TrimSpace(id) == "" {
        return errors.New("remove label: issue ID is required")
    }
    return uc.RemoveLabel(ctx, id, label, actor)
}

Type guard

func hasIssueID(id string) bool { return strings.TrimSpace(id) != "" }

Try / catch

if err := uc.RemoveLabel(ctx, id, label, actor); err != nil {
    if strings.Contains(err.Error(), "id must not be empty") {
        return fmt.Errorf("caller bug: unresolved issue ID")
    }
    return err
}

Prevention

When it happens

Trigger: Calling RemoveLabel(ctx, "", label, actor) or RemoveWispLabel(ctx, "", label, actor) — the ID argument is "", usually from an unpopulated variable or a failed upstream lookup.

Common situations: Scripts where the issue key variable is unset; piping lists into a removal loop where some rows have blank IDs; tests/tools calling the Go API with a placeholder empty ID.

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/c8ecef5a64109516. Report an issue: GitHub.