{"record":{"id":"0ee2457d09101f1f","repo":"gastownhall/beads","slug":"w-apply-batch-item-d-requires-a-dependency-type","errorCode":null,"errorMessage":"%w: apply batch item %d requires a dependency type (max %d chars)","messagePattern":"%w: apply batch item (.+?) requires a dependency type \\(max (.+?) chars\\)","errorType":"validation","errorClass":"issueops.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/storage/batch_apply.go","lineNumber":269,"sourceCode":"// request has already REWRITTEN, and an edge write is a change to the graph\n// rather than to either endpoint's row: the role promises nothing about whether\n// it moves the source's version token. A later guard on that source is\n// therefore left to the substrate, where a genuine mismatch is\n// ErrVersionMismatch — an honest refusal — rather than being refused up front\n// 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","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/batch_apply.go#L251-L287","documentation":"Every DepAdd item requires a dependency Type that passes IsValid() (non-empty and within types.MaxDependencyTypeLen characters). This error fires when the type is missing, empty, or too long. Wraps issueops.ErrValidation.","triggerScenarios":"PlanApplyBatch with DepAdd{Source: ..., Target: ..., Type: \"\"} or a Type longer than types.MaxDependencyTypeLen; types like \"blocks\" are valid, empty or oversized strings are not.","commonSituations":"Forgetting to set Type when constructing DepAdd structs; copying edge data from an export where the type column was blank; a type string that grew past the max length after data changes.","solutions":["Set a valid dependency type (e.g. \"blocks\", \"related\") on the DepAdd item","Truncate or normalize long type values to MaxDependencyTypeLen before batching","Validate the type at edge-generation time and skip/fix invalid rows"],"exampleFix":"// before\nitem := issueops.ApplyItem{Kind: issueops.ItemDepAdd, DepAdd: &issueops.DepAddItem{Source: s, Target: t}}\n// after\nitem := issueops.ApplyItem{Kind: issueops.ItemDepAdd, DepAdd: &issueops.DepAddItem{Source: s, Target: t, Type: \"blocks\"}}","handlingStrategy":"validation","validationCode":"for i, it := range items {\n  if it.DepAdd == nil { continue }\n  if !it.DepAdd.Type.IsValid() {\n    return fmt.Errorf(\"dep item %d has invalid type %q (max %d chars)\", i, it.DepAdd.Type, types.MaxDependencyTypeLen)\n  }\n}","typeGuard":"func hasValidDepType(e *issueops.DepAddItem) bool {\n  return e != nil && e.Type.IsValid()\n}","tryCatchPattern":"if err := store.PlanApplyBatch(plan); err != nil {\n  if errors.Is(err, issueops.ErrValidation) && strings.Contains(err.Error(), \"requires a dependency type\") {\n    // set or fix the Type field and rebuild the plan\n  }\n  return err\n}","preventionTips":["Always set Type when constructing DepAdd items — use a helper","Validate Type.IsValid() at edge-generation time","Enforce the MaxDependencyTypeLen limit in input sanitization"],"tags":["validation","dependency","missing-type","batch-apply"],"backgroundTag":"missing-dependency-type","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}