{"record":{"id":"673cb724fe2175e4","repo":"gastownhall/beads","slug":"w-apply-batch-accepts-at-most-d-items-got-d","errorCode":null,"errorMessage":"%w: apply batch accepts at most %d items, got %d","messagePattern":"%w: apply batch accepts at most (.+?) items, got (.+?)","errorType":"validation","errorClass":"issueops.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/storage/batch_apply.go","lineNumber":77,"sourceCode":"\n// PlanApplyBatch validates an apply-batch request and normalizes its waits-for\n// gate metadata. It is the whole of the role's request validation: every\n// implementation calls it before touching a substrate, so a refused request\n// costs no database work anywhere.\n//\n// THE ORDER OF THE CHECKS IS PART OF THE CONTRACT, because a request can be\n// wrong in several ways at once and a caller fixing them one at a time needs\n// the same answer every time. Request-level shape first, then per-item shape,\n// then the ref graph, then the guards.\nfunc PlanApplyBatch(in issueops.ApplyBatchRequest) (ApplyBatchPlan, error) {\n\tif in.Actor == \"\" {\n\t\treturn ApplyBatchPlan{}, fmt.Errorf(\"%w: apply batch requires an actor\", issueops.ErrValidation)\n\t}\n\tif len(in.Items) == 0 {\n\t\treturn ApplyBatchPlan{}, fmt.Errorf(\"%w: apply batch requires at least one item\", issueops.ErrValidation)\n\t}\n\tif len(in.Items) > issueops.MaxApplyBatchItems {\n\t\treturn ApplyBatchPlan{}, fmt.Errorf(\"%w: apply batch accepts at most %d items, got %d\",\n\t\t\tissueops.ErrValidation, issueops.MaxApplyBatchItems, len(in.Items))\n\t}\n\n\tkeyIndex, err := planApplyBatchKeys(in.Items)\n\tif err != nil {\n\t\treturn ApplyBatchPlan{}, err\n\t}\n\n\tplan := ApplyBatchPlan{\n\t\tActor:                 in.Actor,\n\t\tProvenance:            in.Provenance,\n\t\tForceIDPrefix:         in.ForceIDPrefix,\n\t\tSkipPerEdgeCycleCheck: in.SkipPerEdgeCycleCheck,\n\t\tItems:                 make([]issueops.ApplyItem, len(in.Items)),\n\t\tKeyIndex:              keyIndex,\n\t}\n\tcopy(plan.Items, in.Items)\n","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/batch_apply.go#L59-L95","documentation":"PlanApplyBatch enforces a hard upper bound of issueops.MaxApplyBatchItems per request to keep planning and application bounded. Exceeding it fails with the configured maximum and the actual count, wrapped with issueops.ErrValidation.","triggerScenarios":"Calling PlanApplyBatch with len(Items) > MaxApplyBatchItems — e.g. importing a large export file in one call instead of chunking.","commonSituations":"Bulk imports/migrations from CSV or another tracker; scripts that accumulate all changes without batching; users passing an entire .jsonl export at once.","solutions":["Split the item list into chunks of at most issueops.MaxApplyBatchItems and plan/apply each chunk.","Cap the batch size at call sites by slicing: items[:MaxApplyBatchItems], then continue in a loop.","Reduce batch size at the source (stream or paginate the input) if memory is also a concern."],"exampleFix":"// before\nplan, err := PlanApplyBatch(req{Actor: a, Items: all500Items})\n// after\nfor chunk := range slices.Chunk(all500Items, issueops.MaxApplyBatchItems) {\n    plan, err := PlanApplyBatch(req{Actor: a, Items: chunk})\n    // apply plan...\n}","handlingStrategy":"validation","validationCode":"if len(req.Items) > issueops.MaxApplyBatchItems {\n    return fmt.Errorf(\"batch too large: %d > %d\", len(req.Items), issueops.MaxApplyBatchItems)\n}","typeGuard":null,"tryCatchPattern":"_, err := storage.PlanApplyBatch(req)\nif errors.Is(err, issueops.ErrValidation) && strings.Contains(err.Error(), \"at most\") {\n    return chunkAndApply(req) // split and retry per chunk\n}","preventionTips":["Always chunk bulk imports to MaxApplyBatchItems up front.","Import MaxApplyBatchItems from the library rather than hardcoding limits.","Stream large inputs instead of accumulating one giant slice."],"tags":["validation","batch","limits"],"backgroundTag":"batch-size-limit-exceeded","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}