gastownhall/beads · error · issueops.ErrValidation

%w: apply batch item %d must not carry comments or dependenc

Error message

%w: apply batch item %d must not carry comments or dependencies on the issue; edges are their own items

What it means

In the apply batch model, comments and dependencies attached to a created issue are rejected: edges (comments/dependencies) must be submitted as their own top-level items in the same batch. This error fires when a create item's Issue carries non-empty Comments or Dependencies slices.

Source

Thrown at internal/storage/batch_apply.go:150

		case issueops.ItemClose:
			matches = item.Close != nil
		case issueops.ItemDepAdd:
			matches = item.DepAdd != nil
		default:
			return nil, fmt.Errorf("%w: apply batch item %d has unknown kind %q", issueops.ErrValidation, i, item.Kind)
		}
		if !matches {
			return nil, fmt.Errorf("%w: apply batch item %d is kind %q but carries another kind's payload",
				issueops.ErrValidation, i, item.Kind)
		}
		if item.Create == nil {
			continue
		}
		if item.Create.Issue == nil {
			return nil, fmt.Errorf("%w: apply batch item %d requires an issue", issueops.ErrValidation, i)
		}
		if len(item.Create.Issue.Comments) > 0 || len(item.Create.Issue.Dependencies) > 0 {
			return nil, fmt.Errorf("%w: apply batch item %d must not carry comments or dependencies on the issue; edges are their own items",
				issueops.ErrValidation, i)
		}
		if item.Create.Key == "" {
			continue
		}
		if prior, dup := keyIndex[item.Create.Key]; dup {
			return nil, fmt.Errorf("%w: apply batch item %d reuses key %q, already declared by item %d",
				issueops.ErrValidation, i, item.Create.Key, prior)
		}
		keyIndex[item.Create.Key] = i
	}
	return keyIndex, nil
}

// planApplyBatchItem validates one item's refs, guards and edge metadata, and
// records what it touches for the items after it.
func planApplyBatchItem(item *issueops.ApplyItem, index int, keyIndex map[string]int, touched map[string]bool) error {
	switch item.Kind {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Move each comment into its own item in the batch (create item first, comment items after)
  2. Move each dependency into its own ItemDepAdd item, using keys to reference the created issue
  3. Strip Comments/Dependencies from the Issue before adding it to the batch

Example fix

// before
Create: &issueops.CreateItem{Key: "a", Issue: &types.Issue{Title: "T", Comments: []types.Comment{{Body: "note"}}}}
// after
items := []issueops.ApplyItem{
  {Kind: issueops.ItemCreate, Create: &issueops.CreateItem{Key: "a", Issue: &types.Issue{Title: "T"}}},
  {Kind: issueops.ItemDepAdd, DepAdd: &issueops.DepAddItem{Source: issueops.Ref{Key: "a"}, Target: issueops.Ref{Key: "b"}, Type: "blocks"}},
}
Defensive patterns

Strategy: validation

Validate before calling

for i, it := range items {
  if it.Create != nil && it.Create.Issue != nil &&
     (len(it.Create.Issue.Comments) > 0 || len(it.Create.Issue.Dependencies) > 0) {
    return fmt.Errorf("create item %d carries edges; split them out", i)
  }
}

Type guard

func issueCarriesEdges(is *types.Issue) bool {
  return is != nil && (len(is.Comments) > 0 || len(is.Dependencies) > 0)
}

Try / catch

if err := store.PlanApplyBatch(plan); err != nil {
  if errors.Is(err, issueops.ErrValidation) && strings.Contains(err.Error(), "edges are their own items") {
    // split comments/dependencies into their own batch items and retry
  }
  return err
}

Prevention

When it happens

Trigger: PlanApplyBatch with an item like {Kind: ItemCreate, Create: &CreateItem{Issue: &Issue{Comments: []Comment{...}}}} — i.e. porting an old CreateIssueWithEdges-style call into the batch API without splitting the edges out.

Common situations: Migrating from a single-shot create-with-children API to the batched apply API; flattening a JSON export of an issue with its comments straight into one create item.

Related errors


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