{"record":{"id":"7577fb48275e96aa","repo":"gastownhall/beads","slug":"root-id-must-not-be-empty","errorCode":null,"errorMessage":"root id must not be empty","messagePattern":"root id must not be empty","errorType":"validation","errorClass":"publicops.ErrValidation","httpStatus":null,"severity":"warning","filePath":"internal/storage/issueops/tree_walk.go","lineNumber":27,"sourceCode":")\n\n// The dependency-tree WALK: the shared body behind issueops.TreeWalker on all\n// three backends, split into a pure half and a transactional half so the parts\n// that decide what the answer MEANS are testable without a database.\n//\n// ValidateWalkTreeRequest holds the request vocabulary, PruneTreeByStatus the\n// ancestor-keeping rule and MergeBidirectionalTree the two-walk concatenation;\n// all three are pure and pinned in tree_walk_test.go. WalkDependencyTreeInTx\n// needs a transaction because the root probe, the recursion and the hydration\n// must see one database state, and for a `both` walk that covers BOTH\n// directions.\n\n// ValidateWalkTreeRequest checks a walk request against the vocabulary\n// issueops.WalkTreeRequest documents and returns the normalized direction. It is\n// pure and shared so that all three backends refuse in the same words.\nfunc ValidateWalkTreeRequest(req publicops.WalkTreeRequest) (publicops.TreeDirection, error) {\n\tif req.RootID == \"\" {\n\t\treturn \"\", fmt.Errorf(\"%w: root id must not be empty\", publicops.ErrValidation)\n\t}\n\tdirection := req.Direction\n\tif direction == \"\" {\n\t\tdirection = publicops.TreeDown\n\t}\n\tswitch direction {\n\tcase publicops.TreeDown, publicops.TreeUp, publicops.TreeBoth:\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"%w: direction %q must be one of %q, %q, %q\",\n\t\t\tpublicops.ErrValidation, req.Direction,\n\t\t\tpublicops.TreeDown, publicops.TreeUp, publicops.TreeBoth)\n\t}\n\tif req.MaxDepth < 1 {\n\t\treturn \"\", fmt.Errorf(\"%w: max depth must be at least 1, got %d\", publicops.ErrValidation, req.MaxDepth)\n\t}\n\tif req.MaxRows < 0 {\n\t\treturn \"\", fmt.Errorf(\"%w: max rows must not be negative, got %d\", publicops.ErrValidation, req.MaxRows)\n\t}","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/tree_walk.go#L9-L45","documentation":"ValidateWalkTreeRequest is the pure, shared validation behind WalkDependencyTreeInTx on all backends; it refuses a WalkTreeRequest whose RootID is empty, wrapping publicops.ErrValidation with 'root id must not be empty'. This is a programmer/caller input error, never a database condition — the check runs before any transaction is opened.","triggerScenarios":"Calling WalkDependencyTreeInTx (or the TreeWalker API) with publicops.WalkTreeRequest{RootID: \"\"} — e.g. an unset variable, a struct built from an empty CLI argument, or an ID field that was never populated.","commonSituations":"Scripting `bd dep tree` where the ID argument is missing; passing an issue struct's ID straight through when the issue lookup failed earlier; deserializing a request from JSON that omitted root_id; copy-pasted code paths that forget to set RootID.","solutions":["Set req.RootID to a valid issue ID before calling the walk API.","Use errors.Is(err, publicops.ErrValidation) to detect this and other validation refusals and surface a user-facing 'issue ID required' message.","For CLI paths, validate the ID argument is non-empty before constructing the request.","If the ID comes from a prior lookup, check that lookup's error first — an empty ID often masks an earlier failure."],"exampleFix":"// before\nreq := publicops.WalkTreeRequest{Direction: publicops.TreeDown}\ndir, err := issueops.ValidateWalkTreeRequest(req) // error: root id must not be empty\n// after\nif rootID == \"\" { return fmt.Errorf(\"issue ID required\") }\nreq := publicops.WalkTreeRequest{RootID: rootID, Direction: publicops.TreeDown}\ndir, err := issueops.ValidateWalkTreeRequest(req)","handlingStrategy":"validation","validationCode":"func validWalkRequest(req issueops.WalkTreeRequest) error {\n    if req.RootID == \"\" { return fmt.Errorf(\"root id required\") }\n    return nil\n}","typeGuard":"func hasRootID(req issueops.WalkTreeRequest) bool {\n    return strings.TrimSpace(req.RootID) != \"\"\n}","tryCatchPattern":"dir, err := issueops.ValidateWalkTreeRequest(req)\nif err != nil {\n    if errors.Is(err, publicops.ErrValidation) {\n        return fmt.Errorf(\"invalid tree walk request: %w\", err)\n    }\n    return err\n}","preventionTips":["Check the ID argument is non-empty before building WalkTreeRequest","Propagate errors from prior lookups instead of passing a zero-value ID","Rely on errors.Is(err, ErrValidation) to classify all walk-validation refusals","Validate JSON-deserialized requests for required fields (root_id, MaxDepth >= 1)"],"tags":["go","validation","tree-walk","input"],"backgroundTag":"missing-required-argument","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}