{"record":{"id":"933962d255ef70be","repo":"gofiber/fiber","slug":"paginate-failed-to-encode-cursor-values","errorCode":null,"errorMessage":"paginate: failed to encode cursor values","messagePattern":"paginate: failed to encode cursor values","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/paginate/page_info.go","lineNumber":15,"sourceCode":"package paginate\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"slices\"\n\n\t\"github.com/gofiber/utils/v2\"\n)\n\n// ErrCursorEncode is returned when cursor values cannot be encoded.\nvar ErrCursorEncode = errors.New(\"paginate: failed to encode cursor values\")\n\n// SortOrder represents sort order.\ntype SortOrder string\n\nconst (\n\tASC  SortOrder = \"asc\"\n\tDESC SortOrder = \"desc\"\n)\n\n// SortField represents a sort field with direction.\ntype SortField struct {\n\tField string    `json:\"field\"`\n\tOrder SortOrder `json:\"order\"`\n}\n\n// SortOrderFromString returns a SortOrder from a string (case-insensitive).\nfunc SortOrderFromString(s string) SortOrder {\n\tif utils.EqualFold(s, \"desc\") {","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/paginate/page_info.go#L1-L33","documentation":"Returned by middleware/paginate.PageInfo.SetNextCursor when the cursor value map cannot be JSON-marshaled, or when the resulting base64 cursor exceeds maxCursorLen. It wraps the underlying marshal error via double %w so errors.Is(err, ErrCursorEncode) matches while the cause is still reachable. Cursors must stay small and serializable to be safe in URLs.","triggerScenarios":"Calling SetNextCursor with a map containing values that cannot be JSON-marshaled (channels, funcs, unexported-field structs, cyclic refs) or a map whose encoded form is too large for a cursor token.","commonSituations":"Storing complex domain objects or binary blobs in the cursor instead of just sort keys/IDs; a marshaler (Config.JSONMarshal) that rejects a field type; cursor values growing unbounded as the dataset shape changes.","solutions":["Store only sortable scalar cursor fields (IDs, timestamps), not whole objects.","If a custom JSONMarshal is set, ensure it handles every value type you pass in.","Reduce the number of cursor fields or their size so the encoded token stays under maxCursorLen.","Handle the error in your resolver: fail the request rather than continuing without a cursor."],"exampleFix":"// before\npage.SetNextCursor(map[string]any{\"row\": bigStructWithChan})\n// after\npage.SetNextCursor(map[string]any{\n  \"id\":         row.ID,\n  \"updated_at\": row.UpdatedAt.UnixNano(),\n})","handlingStrategy":"validation","validationCode":"func safeCursor(values map[string]any) error {\n    if _, err := json.Marshal(values); err != nil {\n        return paginate.ErrCursorEncode\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := page.SetNextCursor(values); err != nil {\n    if errors.Is(err, paginate.ErrCursorEncode) {\n        // drop NextCursor and return a page without a cursor, or fail the request\n    }\n    return err\n}","preventionTips":["Put only scalar sort fields (id, timestamp) in cursors, never whole objects.","Ensure a custom JSONMarshal handles every value type used.","Keep cursor size bounded; large cursors risk exceeding URL limits."],"tags":["paginate","cursor","serialization"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}