gastownhall/beads · error · issueops.ErrValidation

%w: apply batch item %d: %v

Error message

%w: apply batch item %d: %v

What it means

The edge metadata on a DepAdd item must pass normalizeApplyEdgeMetadata for its type (valid gate metadata shape/content). When normalization fails, the underlying error is wrapped as %w: apply batch item %d: %v with issueops.ErrValidation. The message's %v carries the specific normalization failure.

Source

Thrown at internal/storage/batch_apply.go:274

// as a request a caller could not have composed.
func planApplyBatchDepAdd(item *issueops.DepAddItem, index int, keyIndex map[string]int) error {
	if err := validateApplyTargetRef(item.Source, index, "source", keyIndex); err != nil {
		return err
	}
	if err := validateApplyTargetRef(item.Target, index, "target", keyIndex); err != nil {
		return err
	}
	if item.Source == item.Target {
		return fmt.Errorf("%w: apply batch item %d: %s cannot depend on itself",
			issueops.ErrSelfDependency, index, applyRefLabel(item.Source))
	}
	if !item.Type.IsValid() {
		return fmt.Errorf("%w: apply batch item %d requires a dependency type (max %d chars)",
			issueops.ErrValidation, index, types.MaxDependencyTypeLen)
	}
	metadata, err := normalizeApplyEdgeMetadata(item.Type, item.Metadata)
	if err != nil {
		return fmt.Errorf("%w: apply batch item %d: %v", issueops.ErrValidation, index, err)
	}
	item.Metadata = metadata
	return nil
}

// checkApplyExpectedVersion refuses a version guard on a row an earlier item of
// this request already mutated.
//
// IT IS A REQUEST-SHAPE RULE, not a race. The token is server-minted and
// rewritten by the write, so mid-request there is no value a caller COULD send:
// the pre-request token is stale by construction, and a row this request just
// created never had one the caller could read. Refusing statically says so;
// letting it through would answer every such request with ErrVersionMismatch
// and leave the caller looking for a concurrent writer that does not exist.
//
// ExpectedStatus and ExpectedAssignee carry no such rule, and the difference is
// that a caller CAN know what its own earlier item set them to.
func checkApplyExpectedVersion(expected *int64, target issueops.Ref, index int, touched map[string]bool) error {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped inner error (%v) for the specific normalization failure and fix the Metadata field accordingly
  2. Omit Metadata (leave nil/empty) if the edge needs no gate
  3. Build metadata via the library's helpers for the given dependency type instead of hand-crafting maps

Example fix

// before
DepAdd: &issueops.DepAddItem{Source: s, Target: t, Type: "blocks", Metadata: map[string]any{"gates": "bogus"}}
// after
DepAdd: &issueops.DepAddItem{Source: s, Target: t, Type: "blocks"} // or metadata built via the gate helper
Defensive patterns

Strategy: validation

Validate before calling

for i, it := range items {
  if it.DepAdd == nil || it.DepAdd.Metadata == nil { continue }
  if _, err := normalizeApplyEdgeMetadata(it.DepAdd.Type, it.DepAdd.Metadata); err != nil {
    return fmt.Errorf("dep item %d: %w", i, err)
  }
}

Type guard

func edgeMetaValid(e *issueops.DepAddItem) bool {
  if e == nil { return false }
  if e.Metadata == nil { return true }
  _, err := normalizeApplyEdgeMetadata(e.Type, e.Metadata)
  return err == nil
}

Try / catch

if err := store.PlanApplyBatch(plan); err != nil {
  if errors.Is(err, issueops.ErrValidation) {
    // the %v suffix carries the specific metadata failure; log it, fix Metadata
  }
  return err
}

Prevention

When it happens

Trigger: PlanApplyBatch with a DepAdd item whose Metadata field violates the rules for its Type (e.g. gate metadata with wrong fields, malformed values, or metadata not permitted for that dependency type).

Common situations: Hand-building gate metadata with wrong key names; copying metadata from a different dependency type that has different rules; library version changes tightening the expected metadata schema.

Related errors


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