gofiber/fiber · error · ErrCursorEncode

paginate: failed to encode cursor values

Error message

paginate: failed to encode cursor values

What it means

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.

Source

Thrown at middleware/paginate/page_info.go:15

package paginate

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"slices"

	"github.com/gofiber/utils/v2"
)

// ErrCursorEncode is returned when cursor values cannot be encoded.
var ErrCursorEncode = errors.New("paginate: failed to encode cursor values")

// SortOrder represents sort order.
type SortOrder string

const (
	ASC  SortOrder = "asc"
	DESC SortOrder = "desc"
)

// SortField represents a sort field with direction.
type SortField struct {
	Field string    `json:"field"`
	Order SortOrder `json:"order"`
}

// SortOrderFromString returns a SortOrder from a string (case-insensitive).
func SortOrderFromString(s string) SortOrder {
	if utils.EqualFold(s, "desc") {

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Keep cursor values minimal — store only the sort key(s) and offset/ID needed to resume, not full objects.
  2. Ensure all values in the cursor map are JSON-serializable (strings, numbers, bools, simple structs).
  3. If you need complex state, store it server-side keyed by a short cursor ID and put only the ID in the cursor.

Example fix

// before — un-serializable / oversized cursor
pageInfo.SetNextCursor(map[string]any{
  "lastItem": largeObject,
  "filter":  func() string { return "..." },
})
// after — minimal, serializable cursor
pageInfo.SetNextCursor(map[string]any{
  "id": lastID,
  "ts": lastTimestamp.Unix(),
})
Defensive patterns

Strategy: validation

Validate before calling

// Validate cursor values are serializable before encoding
for k, v := range cursorValues {
    switch v.(type) {
    case func(), chan struct{}:
        return fmt.Errorf("cursor value %q is not serializable", k)
    }
}

Try / catch

// Handle encode failure gracefully in your handler
if err := pageInfo.SetNextCursor(values); err != nil {
    log.Printf("cursor encode failed: %v", err)
    // proceed without a cursor; client gets no next page
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/933962d255ef70be.json. Report an issue: GitHub.