gofiber/fiber · error · ErrCursorEncode
%w: cursor token exceeds maximum length (%d)
Error message
%w: cursor token exceeds maximum length (%d)
What it means
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.
Source
Thrown at middleware/paginate/page_info.go:217
return values
}
// SetNextCursor encodes a key-value map into an opaque cursor token
// and sets both NextCursor and HasMore on the PageInfo.
func (p *PageInfo) SetNextCursor(values map[string]any) error {
marshal := p.jsonMarshal
if marshal == nil {
marshal = json.Marshal
}
data, err := marshal(values)
if err != nil {
return fmt.Errorf("%w: %w", ErrCursorEncode, err)
}
encoded := base64.RawURLEncoding.EncodeToString(data)
if len(encoded) > maxCursorLen {
return fmt.Errorf("%w: cursor token exceeds maximum length (%d)", ErrCursorEncode, maxCursorLen)
}
p.NextCursor = encoded
p.HasMore = true
return nil
}
View on GitHub (pinned to a105acad6c)
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.
Example fix
// before: entire filter object stored in cursor
values := map[string]any{"filter": bigFilterMap, "id": lastID, "sort": sort}
pi.SetNextCursor(values) // exceeds 2048
// after: store a server-side filter id, keep cursor minimal
filterID := cache.Put(bigFilterMap)
values := map[string]any{"f": filterID, "id": lastID}
pi.SetNextCursor(values) Defensive patterns
Strategy: validation
Validate before calling
// Encode early and check size before committing.
const maxCursorLen = 2048 // mirror paginate.go
func safeSetCursor(pi *paginate.PageInfo, values map[string]any) error {
data, _ := json.Marshal(values)
if len(base64.RawURLEncoding.EncodeToString(data)) > maxCursorLen {
return errors.New("cursor too large; reduce payload")
}
return pi.SetNextCursor(values)
} Try / catch
if err := pi.SetNextCursor(values); err != nil {
if errors.Is(err, paginate.ErrCursorEncode) { /* drop cursor, return last page */ }
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/ec6c8db0f7eb15cd.
Report an issue: GitHub.