{"id":"b7fcf69cdf89e7f6","repo":"gofiber/fiber","slug":"errcursorencode","errorCode":"ErrCursorEncode","errorMessage":"%w: %w","messagePattern":"%w: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/paginate/page_info.go","lineNumber":212,"sourceCode":"\tif err := unmarshal(data, &values); err != nil {\n\t\treturn nil\n\t}\n\n\tp.cursorData = values\n\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":194,"sourceCodeEnd":225,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/paginate/page_info.go#L194-L225","documentation":"Returned by PageInfo.SetNextCursor when the values map cannot be marshaled to JSON. SetNextCursor encodes a caller-supplied map[string]any into an opaque base64 cursor; if json.Marshal fails it wraps the error with the ErrCursorEncode sentinel.","triggerScenarios":"Calling pageInfo.SetNextCursor(values) where values contains a value that json.Marshal cannot encode: a func, chan, complex, a struct with an unexported/non-marshalable field, or a cyclic reference.","commonSituations":"Storing a function or channel in the cursor map; storing a struct with unexported fields; storing a map with a key/value that has a custom MarshalJSON that errors; accidental inclusion of non-serializable domain objects.","solutions":["Only put JSON-serializable primitives (string, number, bool, time-as-string, simple maps/slices) into the cursor values map.","If a custom type must be used, give it a working MarshalJSON or pre-convert it to a plain map.","Test cursor encoding in unit tests with the exact value shapes you paginate on."],"exampleFix":"// before: storing a non-marshalable value\npage.SetNextCursor(map[string]any{\n    \"after\": someFunc, // func value -> marshal error\n})\n\n// after: store a plain serializable value\npage.SetNextCursor(map[string]any{\n    \"after\": lastID, // string/int\n})","handlingStrategy":"validation","validationCode":"// Validate the cursor map is JSON-marshalable before calling SetNextCursor.\nfunc safeSetCursor(p *paginate.PageInfo, values map[string]any) error {\n    if _, err := json.Marshal(values); err != nil {\n        return fmt.Errorf(\"cursor values not serializable: %w\", err)\n    }\n    return p.SetNextCursor(values)\n}","typeGuard":"// isJSONScalar narrows a single value to the types json.Marshal always\n// accepts without a custom marshaler.\nfunc isJSONScalar(v any) bool {\n    switch v.(type) {\n    case string, bool, float64, float32, int, int32, int64,\n        uint, uint32, uint64, json.Number, nil:\n        return true\n    }\n    return false\n}","tryCatchPattern":"if err := page.SetNextCursor(values); err != nil {\n    if errors.Is(err, paginate.ErrCursorEncode) {\n        // fall back to page-based pagination\n        page.HasMore = true\n    }\n}","preventionTips":["Only store JSON primitives and simple maps/slices in cursors.","Unit-test SetNextCursor with real pagination payloads.","Avoid funcs, channels, and unexported-field structs in cursor values."],"tags":["paginate","cursor","json","serialization"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}