{"record":{"id":"ec6c8db0f7eb15cd","repo":"gofiber/fiber","slug":"w-cursor-token-exceeds-maximum-length-d","errorCode":null,"errorMessage":"%w: cursor token exceeds maximum length (%d)","messagePattern":"%w: cursor token exceeds maximum length \\((.+?)\\)","errorType":"validation","errorClass":"ErrCursorEncode","httpStatus":null,"severity":"error","filePath":"middleware/paginate/page_info.go","lineNumber":217,"sourceCode":"\n\treturn values\n}\n\n// SetNextCursor encodes a key-value map into an opaque cursor token\n// and sets both NextCursor and HasMore on the PageInfo.\nfunc (p *PageInfo) SetNextCursor(values map[string]any) error {\n\tmarshal := p.jsonMarshal\n\tif marshal == nil {\n\t\tmarshal = json.Marshal\n\t}\n\tdata, err := marshal(values)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%w: %w\", ErrCursorEncode, err)\n\t}\n\n\tencoded := base64.RawURLEncoding.EncodeToString(data)\n\tif len(encoded) > maxCursorLen {\n\t\treturn fmt.Errorf(\"%w: cursor token exceeds maximum length (%d)\", ErrCursorEncode, maxCursorLen)\n\t}\n\n\tp.NextCursor = encoded\n\tp.HasMore = true\n\n\treturn nil\n}\n","sourceCodeStart":199,"sourceCodeEnd":225,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/paginate/page_info.go#L199-L225","documentation":"After successful JSON marshal, the base64-rawurl-encoded cursor must fit within maxCursorLen (2048 chars, defined in paginate.go:24). If the encoded token exceeds this, SetNextCursor refuses it to keep cursors URL-safe and to bound the size of state stored client-side.","triggerScenarios":"A values map whose JSON form is large enough that its base64 encoding exceeds 2048 chars. Typical: a map with >100 keys, very long string values (e.g. full URLs or BLOBs base64-encoded twice), or deeply nested structures. The threshold is roughly 1.5KB of JSON payload.","commonSituations":"Storing full filter objects or search queries in the cursor; embedding user-supplied free-text in cursor values; paginating over a column set large enough that row identifiers plus sort keys exceed the budget.","solutions":["Reduce the cursor payload to the minimum columns needed to resume: typically the sort key of the last row plus its unique id.","Move large filter state server-side (store a filter id in the cursor, keep the actual filter in a server cache keyed by that id).","If you genuinely need more state, raise the local cap by maintaining a fork or PR — but 2048 is intentionally conservative for URL safety across proxies.","Log len(encoded) at debug level to see how close to the limit you are in production.","Avoid base64-encoding binary data inside cursor values; reference it by id instead."],"exampleFix":"// before: entire filter object stored in cursor\nvalues := map[string]any{\"filter\": bigFilterMap, \"id\": lastID, \"sort\": sort}\npi.SetNextCursor(values) // exceeds 2048\n\n// after: store a server-side filter id, keep cursor minimal\nfilterID := cache.Put(bigFilterMap)\nvalues := map[string]any{\"f\": filterID, \"id\": lastID}\npi.SetNextCursor(values)","handlingStrategy":"validation","validationCode":"// Encode early and check size before committing.\nconst maxCursorLen = 2048 // mirror paginate.go\nfunc safeSetCursor(pi *paginate.PageInfo, values map[string]any) error {\n  data, _ := json.Marshal(values)\n  if len(base64.RawURLEncoding.EncodeToString(data)) > maxCursorLen {\n    return errors.New(\"cursor too large; reduce payload\")\n  }\n  return pi.SetNextCursor(values)\n}","typeGuard":null,"tryCatchPattern":"if err := pi.SetNextCursor(values); err != nil {\n  if errors.Is(err, paginate.ErrCursorEncode) { /* drop cursor, return last page */ }\n}","preventionTips":["Keep cursor payloads to the minimum resume keys (sort key + unique id).","Move large filter state server-side; store only a reference in the cursor.","Log encoded cursor length at debug level to monitor headroom.","Avoid embedding base64 blobs inside cursor values."],"tags":["paginate","cursor","size-limit","url-safety"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}