gastownhall/beads · error · issueops.ErrValidation

%w: apply batch item %d: %s must name a key or an id

Error message

%w: apply batch item %d: %s must name a key or an id

What it means

This error is thrown by validateApplyRef when an item in an apply batch references an issue (as a dependency endpoint or target) without naming it: both ref.Key and ref.ID are empty. The batch applier needs exactly one identifier to resolve the ref, so it rejects the item at plan time with a wrapped issueops.ErrValidation.

Source

Thrown at internal/storage/batch_apply.go:324

	}
	if ref.Key == "" {
		return nil
	}
	declaredAt, ok := keyIndex[ref.Key]
	if !ok {
		return &issueops.RefError{Index: index, Member: member, Key: ref.Key}
	}
	if declaredAt >= index {
		return &issueops.RefError{Index: index, Member: member, Key: ref.Key, DeclaredLater: true}
	}
	return nil
}

// validateApplyRef checks the exactly-one rule every ref answers to.
func validateApplyRef(ref issueops.Ref, index int, member string) error {
	switch {
	case ref.Key == "" && ref.ID == "":
		return fmt.Errorf("%w: apply batch item %d: %s must name a key or an id", issueops.ErrValidation, index, member)
	case ref.Key != "" && ref.ID != "":
		return fmt.Errorf("%w: apply batch item %d: %s names both key %q and id %q; exactly one",
			issueops.ErrValidation, index, member, ref.Key, ref.ID)
	}
	return nil
}

// applyTouchKeyRef renders a ref as the address the touched set is keyed by.
// The two namespaces are kept apart so a key and an id that happen to read the
// same string are not confused for one row.
func applyTouchKeyRef(ref issueops.Ref) string {
	if ref.Key != "" {
		return "key:" + ref.Key
	}
	return "id:" + ref.ID
}

// applyRefLabel renders a ref for a message.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Populate exactly one of ref.Key or ref.ID on every batch item before calling the apply API
  2. Validate all refs client-side before building the batch
  3. Check the payload/JSON actually carries the key or id field for each item

Example fix

// before
refs := []issueops.Ref{{}}
// after
refs := []issueops.Ref{{Key: "beads-42"}}
Defensive patterns

Strategy: validation

Validate before calling

func validateRef(r issueops.Ref, i int, member string) error {
	if r.Key == "" && r.ID == "" {
		return fmt.Errorf("item %d: %s needs a key or id", i, member)
	}
	return nil
}

Type guard

func refNamed(r issueops.Ref) bool { return r.Key != "" || r.ID != "" }

Prevention

When it happens

Trigger: Calling a batch apply API (planApplyBatchCreate path, or a dep/target ref validated via validateApplyTargetRef) with a Ref struct where both Key and ID are the empty string.

Common situations: Constructing batch items programmatically from structs where the ref field was never populated; deserializing JSON payloads that omit the key/id fields; copying a Ref after clearing fields; passing a zero-value Ref.

Related errors


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