{"record":{"id":"f01032aaefa221cc","repo":"vitessio/vitess","slug":"mismatched-cutoff-and-label-lengths","errorCode":null,"errorMessage":"mismatched cutoff and label lengths","messagePattern":"mismatched cutoff and label lengths","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/stats/histogram.go","lineNumber":61,"sourceCode":"// based on the cutoffs. The buckets are categorized using the\n// following criterion: cutoff[i-1] < value <= cutoff[i]. Anything\n// higher than the highest cutoff is labeled as \"inf\".\nfunc NewHistogram(name, help string, cutoffs []int64) *Histogram {\n\tlabels := make([]string, len(cutoffs)+1)\n\tfor i, v := range cutoffs {\n\t\tlabels[i] = strconv.FormatInt(v, 10)\n\t}\n\tlabels[len(labels)-1] = \"inf\"\n\treturn NewGenericHistogram(name, help, cutoffs, labels, \"Count\", \"Total\")\n}\n\n// NewGenericHistogram creates a histogram where all the labels are\n// supplied by the caller. The number of labels has to be one more than\n// the number of cutoffs because the last label captures everything that\n// exceeds the highest cutoff.\nfunc NewGenericHistogram(name, help string, cutoffs []int64, labels []string, countLabel, totalLabel string) *Histogram {\n\tif len(cutoffs) != len(labels)-1 {\n\t\tpanic(\"mismatched cutoff and label lengths\")\n\t}\n\th := &Histogram{\n\t\tname:       name,\n\t\thelp:       help,\n\t\tcutoffs:    cutoffs,\n\t\tlabels:     labels,\n\t\tcountLabel: countLabel,\n\t\ttotalLabel: totalLabel,\n\t\tbuckets:    make([]atomic.Int64, len(labels)),\n\t}\n\tif name != \"\" {\n\t\tpublish(name, h)\n\t}\n\treturn h\n}\n\n// Adds a hook that will be called every time a new value is added to the histogram\nfunc (h *Histogram) AddHook(hook func(int64)) {","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/stats/histogram.go#L43-L79","documentation":"NewGenericHistogram requires the labels slice to be exactly one longer than the cutoffs slice: one label per cutoff bucket plus a final label that captures everything above the highest cutoff. The library panics immediately when the counts do not satisfy this invariant.","triggerScenarios":"Calling stats.NewGenericHistogram(name, help, cutoffs, labels, ...) with len(cutoffs) != len(labels)-1, e.g. 4 cutoffs with 4 or 6 labels.","commonSituations":"Hand-editing a cutoffs list without updating labels; generating labels programmatically and off-by-one; copying a predefined label set but trimming the catch-all last label.","solutions":["Make len(labels) == len(cutoffs)+1, with the last label the overflow bucket (commonly \"unlimited\").","Recount the arrays at the call site; ensure every new cutoff gets a matching label plus keep the final catch-all label.","Define cutoffs and labels together as a single paired constant/slice to avoid drift."],"exampleFix":"// before\ncutoffs := []int64{1, 10, 100}\nlabels := []string{\"0-1\", \"1-10\", \"10-100\"} // panics: missing catch-all\n// after\nlabels := []string{\"0-1\", \"1-10\", \"10-100\", \"unlimited\"}","handlingStrategy":"validation","validationCode":"if len(labels) != len(cutoffs)+1 {\n    return fmt.Errorf(\"histogram %s: need %d labels, got %d\", name, len(cutoffs)+1, len(labels))\n}\nh := stats.NewGenericHistogram(name, help, cutoffs, labels, countLabel, totalLabel)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Define cutoffs and the trailing catch-all label together in one constant block.","Include the overflow label (e.g. \"unlimited\") whenever you add a cutoff.","Add a package-level test asserting label count for every predefined histogram."],"tags":["go","stats","panic","histogram","length-mismatch"],"backgroundTag":"length-mismatch","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}