{"record":{"id":"28862ae087d78040","repo":"gofiber/fiber","slug":"betweenlen-constraint-requires-two-arguments","errorCode":null,"errorMessage":"betweenLen constraint requires two arguments","messagePattern":"betweenLen constraint requires two arguments","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"constraint.go","lineNumber":415,"sourceCode":"\nfunc (lenConstraintType) Execute(param string, data []any) bool {\n\tif len(data) == 0 {\n\t\treturn false\n\t}\n\tlimit, ok := data[0].(int)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn len(param) == limit\n}\n\ntype betweenLenConstraintType struct{}\n\nfunc (betweenLenConstraintType) Name() string { return ConstraintBetweenLen }\nfunc (betweenLenConstraintType) Analyze(args []string) ([]any, error) {\n\targs = parseConstraintArgs(args)\n\tif len(args) < 2 {\n\t\treturn nil, errors.New(\"betweenLen constraint requires two arguments\")\n\t}\n\tlo, err := strconv.Atoi(args[0])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse constraint arg: %w\", err)\n\t}\n\thi, err := strconv.Atoi(args[1])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse constraint arg: %w\", err)\n\t}\n\treturn []any{lo, hi}, nil\n}\n\nfunc (betweenLenConstraintType) Execute(param string, data []any) bool {\n\tif len(data) < 2 {\n\t\treturn false\n\t}\n\tlo, ok := data[0].(int)\n\tif !ok {","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/constraint.go#L397-L433","documentation":"The betweenLen route-parameter constraint requires that the length of a matched path segment fall within an inclusive [lo, hi] range. It is declared inline in a route pattern as :param<betweenLen(lo,hi)>. Its Analyze method (part of the public ConstraintAnalyzer interface, constraint.go:84-87) needs exactly two integer arguments; with fewer than two it returns this error. Note that fiber's built-in registration path (newConstraint at constraint.go:188) currently swallows Analyze errors, so this surfaces when callers invoke Analyze directly or via a custom-constraint wrapper that delegates to it (constraint.go:105).","triggerScenarios":"Calling Analyze on a betweenLen constraint with fewer than two comma-separated arguments. This happens when a route pattern is written as :name<betweenLen(5)> (one arg), :name<betweenLen()> (empty), :name<betweenLen> (no parens), or :name<betweenLen(2\\,10)> (escaped comma collapses two args into one). Also returned when a custom constraint's Analyze delegates to betweenLenConstraintType through the customConstraintWrapper.","commonSituations":"Typos in route definitions; copy-paste from minLen/maxLen forgetting the second bound; escaping the comma separator inside the args list; migrating from a single-bound length constraint to a ranged one.","solutions":["Provide two integer arguments: :param<betweenLen(2,10)>.","If only one bound is needed, switch to :param<minLen(n)> or :param<maxLen(n)> instead.","Ensure the comma is not escaped (an escaped comma \\, becomes part of a single argument).","Add a startup unit test that drives a request through App.Test to confirm the constraint behaves as intended."],"exampleFix":"// before\napp.Get(\"/u/:name<betweenLen(3)>\", h)\n// after\napp.Get(\"/u/:name<betweenLen(3,20)>\", h)","handlingStrategy":"validation","validationCode":"// Unit-test every route pattern at startup. The built-in registration path\n// silently swallows Analyze errors, so drive a representative request instead.\nfunc TestRoutePatterns(t *testing.T) {\n    cases := []struct{ path, param string }{\n        {\"/u/:name<betweenLen(3,20)>\", \"abc\"},\n    }\n    for _, tc := range cases {\n        app := fiber.New()\n        app.Get(tc.path, func(c fiber.Ctx) error { return c.SendString(\"ok\") })\n        req := httptest.NewRequest(\"GET\", strings.Replace(tc.path, \":name\", tc.param, 1), nil)\n        resp, _ := app.Test(req)\n        if resp.StatusCode != 200 {\n            t.Fatalf(\"pattern %s rejected valid param\", tc.path)\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"// When calling Analyze directly on a ConstraintAnalyzer (e.g. inside a custom\n// constraint that delegates), propagate the error rather than ignoring it.\ntyped, err := analyzer.Analyze(args)\nif err != nil {\n    return nil, fmt.Errorf(\"init route constraint: %w\", err)\n}","preventionTips":["Keep all route patterns in one registry and lint that betweenLen/range have exactly two comma-separated args.","Prefer minLen/maxLen for single-bound length checks to avoid the 2-arg requirement.","Add an App.Test-driven unit test per constrained route so malformed constraints surface during CI."],"tags":["routing","constraint","route-registration","validation"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}