{"record":{"id":"31af02cefb685f6c","repo":"tsenart/vegeta","slug":"lttb-min-threshold-is-3","errorCode":null,"errorMessage":"lttb: min threshold is 3","messagePattern":"lttb: min threshold is 3","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/lttb/lttb.go","lineNumber":27,"sourceCode":"// count number of Points or an error.\ntype Iter func(count int) ([]Point, error)\n\n// Downsample `count` number of data points retrieved from the given iterator\n// function to contain only `threshold` number of points while maintaining close\n// visual similarity to the original data. The algorithm is called\n// Largest-Triangle-Three-Buckets and is described in:\n// https://skemman.is/bitstream/1946/15343/3/SS_MSthesis.pdf\n//\n// This implementation grew out of https://github.com/dgryski/go-lttb\n// to limit memory usage by leveraging iterators.\nfunc Downsample(count, threshold int, it Iter) ([]Point, error) {\n\tif threshold >= count || threshold == 0 {\n\t\tpoints, err := it(count)\n\t\treturn points, err\n\t}\n\n\tif threshold < 3 {\n\t\treturn nil, errors.New(\"lttb: min threshold is 3\")\n\t}\n\n\t// Bucket size. Leave room for start and end data points\n\tsize := float64(count-2) / float64(threshold-2)\n\n\t// Get the first point and the current bucket.\n\tpoints, err := it(int(1 + size))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsamples := make([]Point, 0, threshold)\n\tsamples = append(samples, points[0]) // Always add the first point\n\tcurrent := points[1:]\n\n\tfor i := 0; i < threshold-2; i++ {\n\t\t// Calculate bucket boundaries (non inclusive hi)\n\t\tlo := int(float64(i+1)*size) + 1","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/tsenart/vegeta/blob/cf5811269046c672a604b1eb352204d30f16ae4a/lib/lttb/lttb.go#L9-L45","documentation":"Downsample in lib/lttb rejects thresholds below 3 because Largest-Triangle-Three-Buckets requires at least three buckets (first point, intermediate buckets, last point). Smaller thresholds cannot produce a meaningful downsampling.","triggerScenarios":"Calling lttb.Downsample(data, threshold) with threshold 1 or 2 (but not 0, which takes the earlier pass-through branch).","commonSituations":"Computing a target point count from plot dimensions or user input that rounds down to 1–2, or hardcoding a tiny threshold in tests/scripts.","solutions":["Use a threshold of at least 3.","Guard the call site: if threshold < 3, either skip downsampling or clamp it to 3.","If the input data itself is shorter than the threshold, note the count<=threshold branch returns data unchanged, so the error only concerns the threshold value."],"exampleFix":"// before\npoints, err := lttb.Downsample(data, 2)\n// after\nif t < 3 { t = 3 }\npoints, err := lttb.Downsample(data, t)","handlingStrategy":"validation","validationCode":"if threshold < 3 {\n    threshold = 3\n}\npoints, err := lttb.Downsample(data, threshold)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clamp user-provided downsampling targets to >= 3","Remember thresholds >= count or 0 are no-ops, only 1-2 error","Add input validation at the plotting config layer"],"tags":["algorithm","downsampling","validation"],"backgroundTag":"parameter-out-of-range","analyzedSha":"cf5811269046c672a604b1eb352204d30f16ae4a","analyzedAt":"2026-08-31T11:04:20.464Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}