{"record":{"id":"5dba285550d4aaa0","repo":"gastownhall/beads","slug":"w-apply-batch-item-d-v","errorCode":null,"errorMessage":"%w: apply batch item %d: %v","messagePattern":"%w: apply batch item (.+?): (.+?)","errorType":"validation","errorClass":"issueops.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/storage/batch_apply.go","lineNumber":274,"sourceCode":"// as a request a caller could not have composed.\nfunc planApplyBatchDepAdd(item *issueops.DepAddItem, index int, keyIndex map[string]int) error {\n\tif err := validateApplyTargetRef(item.Source, index, \"source\", keyIndex); err != nil {\n\t\treturn err\n\t}\n\tif err := validateApplyTargetRef(item.Target, index, \"target\", keyIndex); err != nil {\n\t\treturn err\n\t}\n\tif item.Source == item.Target {\n\t\treturn fmt.Errorf(\"%w: apply batch item %d: %s cannot depend on itself\",\n\t\t\tissueops.ErrSelfDependency, index, applyRefLabel(item.Source))\n\t}\n\tif !item.Type.IsValid() {\n\t\treturn fmt.Errorf(\"%w: apply batch item %d requires a dependency type (max %d chars)\",\n\t\t\tissueops.ErrValidation, index, types.MaxDependencyTypeLen)\n\t}\n\tmetadata, err := normalizeApplyEdgeMetadata(item.Type, item.Metadata)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%w: apply batch item %d: %v\", issueops.ErrValidation, index, err)\n\t}\n\titem.Metadata = metadata\n\treturn nil\n}\n\n// checkApplyExpectedVersion refuses a version guard on a row an earlier item of\n// this request already mutated.\n//\n// IT IS A REQUEST-SHAPE RULE, not a race. The token is server-minted and\n// rewritten by the write, so mid-request there is no value a caller COULD send:\n// the pre-request token is stale by construction, and a row this request just\n// created never had one the caller could read. Refusing statically says so;\n// letting it through would answer every such request with ErrVersionMismatch\n// and leave the caller looking for a concurrent writer that does not exist.\n//\n// ExpectedStatus and ExpectedAssignee carry no such rule, and the difference is\n// that a caller CAN know what its own earlier item set them to.\nfunc checkApplyExpectedVersion(expected *int64, target issueops.Ref, index int, touched map[string]bool) error {","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/batch_apply.go#L256-L292","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Read the wrapped inner error (%v) for the specific normalization failure and fix the Metadata field accordingly","Omit Metadata (leave nil/empty) if the edge needs no gate","Build metadata via the library's helpers for the given dependency type instead of hand-crafting maps"],"exampleFix":"// before\nDepAdd: &issueops.DepAddItem{Source: s, Target: t, Type: \"blocks\", Metadata: map[string]any{\"gates\": \"bogus\"}}\n// after\nDepAdd: &issueops.DepAddItem{Source: s, Target: t, Type: \"blocks\"} // or metadata built via the gate helper","handlingStrategy":"validation","validationCode":"for i, it := range items {\n  if it.DepAdd == nil || it.DepAdd.Metadata == nil { continue }\n  if _, err := normalizeApplyEdgeMetadata(it.DepAdd.Type, it.DepAdd.Metadata); err != nil {\n    return fmt.Errorf(\"dep item %d: %w\", i, err)\n  }\n}","typeGuard":"func edgeMetaValid(e *issueops.DepAddItem) bool {\n  if e == nil { return false }\n  if e.Metadata == nil { return true }\n  _, err := normalizeApplyEdgeMetadata(e.Type, e.Metadata)\n  return err == nil\n}","tryCatchPattern":"if err := store.PlanApplyBatch(plan); err != nil {\n  if errors.Is(err, issueops.ErrValidation) {\n    // the %v suffix carries the specific metadata failure; log it, fix Metadata\n  }\n  return err\n}","preventionTips":["Build edge metadata with the library's gate helpers, not raw maps","Leave Metadata unset for plain edges","Pin library versions and review metadata-schema changes when upgrading"],"tags":["validation","dependency","metadata","batch-apply"],"backgroundTag":"edge-metadata-invalid","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}