{"record":{"id":"62a709b95305d3ca","repo":"gastownhall/beads","slug":"too-many-custom-statuses-d-maximum-is-d","errorCode":null,"errorMessage":"too many custom statuses (%d): maximum is %d","messagePattern":"too many custom statuses \\((.+?)\\): maximum is (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/types/types.go","lineNumber":658,"sourceCode":"\n\t\tif !statusNameRegexp.MatchString(name) {\n\t\t\treturn nil, fmt.Errorf(\"invalid status name %q: must match [a-z][a-z0-9_-]* (lowercase, letter-first, no spaces)\", name)\n\t\t}\n\n\t\tif builtInStatusNames[strings.ToLower(name)] {\n\t\t\treturn nil, fmt.Errorf(\"custom status %q collides with built-in status\", name)\n\t\t}\n\n\t\tif seen[name] {\n\t\t\treturn nil, fmt.Errorf(\"duplicate custom status name %q\", name)\n\t\t}\n\t\tseen[name] = true\n\n\t\tresult = append(result, CustomStatus{Name: name, Category: category})\n\t}\n\n\tif len(result) > maxCustomStatuses {\n\t\treturn nil, fmt.Errorf(\"too many custom statuses (%d): maximum is %d\", len(result), maxCustomStatuses)\n\t}\n\n\treturn result, nil\n}\n\n// CustomStatusNames extracts just the name strings from a slice of CustomStatus.\n// Useful for backward-compatible callers that only need names for validation.\nfunc CustomStatusNames(statuses []CustomStatus) []string {\n\tif len(statuses) == 0 {\n\t\treturn nil\n\t}\n\tnames := make([]string, len(statuses))\n\tfor i, s := range statuses {\n\t\tnames[i] = s.Name\n\t}\n\treturn names\n}\n","sourceCodeStart":640,"sourceCodeEnd":676,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/types/types.go#L640-L676","documentation":"The parser enforces a hard cap (maxCustomStatuses) on how many custom statuses can be defined. Exceeding it aborts parsing. The cap keeps status enums, indexes, and UI rendering bounded.","triggerScenarios":"Supplying more than maxCustomStatuses entries in the custom statuses list passed to the parsing function in types.go.","commonSituations":"Bulk-importing statuses from another tracker; generated config with a status per team or per workflow stage; looping scripts that append statuses without checking the total.","solutions":["Reduce the number of custom statuses to at most maxCustomStatuses","Consolidate near-duplicate statuses (e.g. merge 'in-review-2' into 'in-review')","Split workflows across projects if each genuinely needs its own status set"],"exampleFix":"// before\nstatuses := make([]CustomStatus, 0, 200) // pushes past the cap\n// after\nif len(statuses) > maxCustomStatuses { statuses = statuses[:maxCustomStatuses] }","handlingStrategy":"validation","validationCode":"if len(statuses) > maxCustomStatuses {\n    return fmt.Errorf(\"need at most %d statuses, have %d\", maxCustomStatuses, len(statuses))\n}","typeGuard":null,"tryCatchPattern":"statuses, err := ParseCustomStatuses(raw)\nif err != nil && strings.Contains(err.Error(), \"too many custom statuses\") {\n    // trim/merge statuses before retrying\n}","preventionTips":["Check len(statuses) against maxCustomStatuses before generating config","Consolidate similar statuses instead of adding new ones per workflow stage","Avoid bulk-importing full status sets from other trackers"],"tags":["config","limits","custom-status"],"backgroundTag":"custom-status-limit-exceeded","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}