{"record":{"id":"a244757451842f49","repo":"gastownhall/beads","slug":"max-rows-must-not-be-negative-got-d","errorCode":null,"errorMessage":"max rows must not be negative, got %d","messagePattern":"max rows must not be negative, got (.+?)","errorType":"validation","errorClass":"publicops.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/tree_walk.go","lineNumber":44,"sourceCode":"\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}\n\treturn direction, nil\n}\n\n// PruneTreeByStatus keeps every node carrying status, plus the ancestor chain of\n// each survivor, in the walk order the nodes arrived in.\n//\n// KEEPING THE ANCESTORS IS WHAT MAKES THE ANSWER STILL A TREE. A bare filter\n// would return nodes whose ParentID names something absent from the answer, and\n// every renderer that rebuilds the shape from Depth and ParentID would then draw\n// orphans at the wrong indentation.\n//\n// A PRUNE THAT MATCHES NOTHING RETURNS NOTHING, root included. The root survives\n// only as somebody's ancestor, never for its own sake, so a tree with no\n// matching member comes back empty rather than as a lone root — see\n// issueops.WalkTreeRequest.Status, which states it as a promise.\nfunc PruneTreeByStatus(nodes []*types.TreeNode, status types.Status) []*types.TreeNode {\n\tif len(nodes) == 0 {","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/tree_walk.go#L26-L62","documentation":"ValidateWalkTreeRequest rejects a negative MaxRows with this ErrValidation-wrapped error. The library throws it because a negative row cap is meaningless for a result-limiting field; use 0 (treated as the library's no-limit/default) or a positive cap.","triggerScenarios":"Calling WalkDependencyTreeInTx with req.MaxRows < 0, e.g. -1 used as an \"unlimited\" sentinel carried over from a different API's convention.","commonSituations":"Using -1 for \"no limit\" per another library's convention; computing MaxRows via subtraction that can go negative; misparsed config values.","solutions":["Set req.MaxRows to 0 to mean unlimited, or a positive integer to cap results.","Clamp computed values: if maxRows < 0 { maxRows = 0 }.","Update code that uses the -1-as-unlimited convention to this library's 0-means-unlimited convention."],"exampleFix":"// before\nreq.MaxRows = -1 // \"unlimited\"\n// after\nreq.MaxRows = 0 // 0 means no row limit","handlingStrategy":"validation","validationCode":"if req.MaxRows < 0 {\n\treq.MaxRows = 0\n}","typeGuard":null,"tryCatchPattern":"if err := walk(ctx, req); errors.Is(err, publicops.ErrValidation) {\n\t// fix MaxRows; validation errors are not retryable as-is\n}","preventionTips":["Normalize limit fields at construction time (negative -> 0).","Centralize request-building in one helper so clamping is applied consistently.","Document the 0-means-unlimited convention so -1 is not reintroduced."],"tags":["validation","dependency-tree","parameter-range"],"backgroundTag":"parameter-out-of-range","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}