{"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":"exception","errorClass":"ErrCursorEncode","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/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/paginate/page_info.go#L1-L33","documentation":"Returned by paginate.PageInfo.SetNextCursor (page_info.go:15, 212, 217) when the cursor values map cannot be JSON-marshaled, or when the resulting base64 cursor exceeds maxCursorLen. SetNextCursor serializes the provided map[string]any to JSON, base64-encodes it, and sets NextCursor/HasMore. A marshaling error (e.g. a value containing a channel, func, or a recursive struct) or an oversized cursor triggers this wrapped error.","triggerScenarios":"Calling pageInfo.SetNextCursor(map) with a value that json.Marshal cannot handle (functions, channels, cyclic references), or with a very large map whose encoded form exceeds the internal maxCursorLen constant. This happens in handler code building pagination responses.","commonSituations":"Storing un-serializable types (time.Duration as a func, channels) in cursor values; embedding large objects or binary blobs in the cursor instead of just an ID/offset; cursor values growing unbounded (e.g. accumulating filter state).","solutions":["Keep cursor values minimal — store only the sort key(s) and offset/ID needed to resume, not full objects.","Ensure all values in the cursor map are JSON-serializable (strings, numbers, bools, simple structs).","If you need complex state, store it server-side keyed by a short cursor ID and put only the ID in the cursor."],"exampleFix":"// before — un-serializable / oversized cursor\npageInfo.SetNextCursor(map[string]any{\n  \"lastItem\": largeObject,\n  \"filter\":  func() string { return \"...\" },\n})\n// after — minimal, serializable cursor\npageInfo.SetNextCursor(map[string]any{\n  \"id\": lastID,\n  \"ts\": lastTimestamp.Unix(),\n})","handlingStrategy":"validation","validationCode":"// Validate cursor values are serializable before encoding\nfor k, v := range cursorValues {\n    switch v.(type) {\n    case func(), chan struct{}:\n        return fmt.Errorf(\"cursor value %q is not serializable\", k)\n    }\n}","typeGuard":null,"tryCatchPattern":"// Handle encode failure gracefully in your handler\nif err := pageInfo.SetNextCursor(values); err != nil {\n    log.Printf(\"cursor encode failed: %v\", err)\n    // proceed without a cursor; client gets no next page\n}","preventionTips":["Store only primitive serializable values (string, int, float) in cursors.","Keep cursors minimal — just the sort key and offset.","Store complex state server-side and reference it by a short ID in the cursor."],"tags":["paginate","serialization","cursor","json"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}