{"record":{"id":"b6570f239b982bb8","repo":"gastownhall/beads","slug":"w-apply-batch-item-d-is-kind-q-but-carries-ano","errorCode":null,"errorMessage":"%w: apply batch item %d is kind %q but carries another kind's payload","messagePattern":"%w: apply batch item (.+?) is kind %q but carries another kind's payload","errorType":"validation","errorClass":"issueops.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/storage/batch_apply.go","lineNumber":140,"sourceCode":"\t\tif payloads != 1 {\n\t\t\treturn nil, fmt.Errorf(\"%w: apply batch item %d must carry exactly one payload, got %d\",\n\t\t\t\tissueops.ErrValidation, i, payloads)\n\t\t}\n\t\tvar matches bool\n\t\tswitch item.Kind {\n\t\tcase issueops.ItemCreate:\n\t\t\tmatches = item.Create != nil\n\t\tcase issueops.ItemUpdate:\n\t\t\tmatches = item.Update != nil\n\t\tcase issueops.ItemClose:\n\t\t\tmatches = item.Close != nil\n\t\tcase issueops.ItemDepAdd:\n\t\t\tmatches = item.DepAdd != nil\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"%w: apply batch item %d has unknown kind %q\", issueops.ErrValidation, i, item.Kind)\n\t\t}\n\t\tif !matches {\n\t\t\treturn nil, fmt.Errorf(\"%w: apply batch item %d is kind %q but carries another kind's payload\",\n\t\t\t\tissueops.ErrValidation, i, item.Kind)\n\t\t}\n\t\tif item.Create == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif item.Create.Issue == nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: apply batch item %d requires an issue\", issueops.ErrValidation, i)\n\t\t}\n\t\tif len(item.Create.Issue.Comments) > 0 || len(item.Create.Issue.Dependencies) > 0 {\n\t\t\treturn nil, fmt.Errorf(\"%w: apply batch item %d must not carry comments or dependencies on the issue; edges are their own items\",\n\t\t\t\tissueops.ErrValidation, i)\n\t\t}\n\t\tif item.Create.Key == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif prior, dup := keyIndex[item.Create.Key]; dup {\n\t\t\treturn nil, fmt.Errorf(\"%w: apply batch item %d reuses key %q, already declared by item %d\",\n\t\t\t\tissueops.ErrValidation, i, item.Create.Key, prior)","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/batch_apply.go#L122-L158","documentation":"PlanApplyBatch validates that each ApplyItem's Kind matches exactly one non-nil payload (Create/Update/Close/DepAdd). This error means the item declared one kind but the payload pointer set belongs to a different kind, so the planner cannot tell what operation the item represents. It wraps issueops.ErrValidation, so callers can detect it via errors.Is.","triggerScenarios":"Calling PlanApplyBatch (via planApplyBatchKeys) with an item like {Kind: ItemCreate, Update: &UpdateItem{...}} — the kind label and the populated payload struct disagree.","commonSituations":"Copy-pasting item construction code and editing the payload but not the Kind field; building items from JSON/config where kind and payload come from different sources; switching an item from update to create and forgetting to nil the old payload.","solutions":["Set item.Kind to match the one populated payload field (or vice versa)","Nil out the extra payload so exactly one of Create/Update/Close/DepAdd is set, matching Kind","Write a small builder helper per kind (NewItemCreate/NewItemDepAdd) that sets Kind and payload together"],"exampleFix":"// before\nitem := issueops.ApplyItem{Kind: issueops.ItemUpdate, DepAdd: &edge}\n// after\nitem := issueops.ApplyItem{Kind: issueops.ItemDepAdd, DepAdd: &edge}","handlingStrategy":"validation","validationCode":"for i, it := range items {\n  var n int\n  if it.Create != nil { n++ }\n  if it.Update != nil { n++ }\n  if it.Close != nil { n++ }\n  if it.DepAdd != nil { n++ }\n  if n != 1 { return fmt.Errorf(\"item %d: %d payloads\", i, n) }\n  var ok bool\n  switch it.Kind {\n  case issueops.ItemCreate: ok = it.Create != nil\n  case issueops.ItemUpdate: ok = it.Update != nil\n  case issueops.ItemClose: ok = it.Close != nil\n  case issueops.ItemDepAdd: ok = it.DepAdd != nil\n  default: return fmt.Errorf(\"item %d: unknown kind\", i)\n  }\n  if !ok { return fmt.Errorf(\"item %d: kind/payload mismatch\", i) }\n}","typeGuard":"func kindMatchesPayload(it issueops.ApplyItem) bool {\n  switch it.Kind {\n  case issueops.ItemCreate: return it.Create != nil\n  case issueops.ItemUpdate: return it.Update != nil\n  case issueops.ItemClose: return it.Close != nil\n  case issueops.ItemDepAdd: return it.DepAdd != nil\n  }\n  return false\n}","tryCatchPattern":"if err := store.PlanApplyBatch(plan); err != nil {\n  if errors.Is(err, issueops.ErrValidation) {\n    // fix the item's kind/payload pairing and rebuild the plan\n  }\n  return err\n}","preventionTips":["Use per-kind constructor helpers that set Kind and payload together","Never assign more than one payload field on an ApplyItem","Add a pre-submit validation pass mirroring the planner's checks"],"tags":["validation","batch-apply","kind-payload-mismatch"],"backgroundTag":"batch-item-kind-payload-mismatch","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}