JuliusBrussee/caveman · error · ErrPagingUnsupported

objectstore: paged listing unsupported

Error message

objectstore: paged listing unsupported

What it means

objectstore.ErrPagingUnsupported is returned when paged listing (ListPage or the ListPage helper) is called on a backend that does not implement the PagedLister cursor interface, or when a Memory-backend ListPage is given maxKeys <= 0. Callers that must bound listing memory need a cursor-capable backend; this error makes that requirement explicit.

Solutions

  1. Use a backend that implements PagedLister (the MinIO/S3 store) wherever bounded listing is required.
  2. If using Memory, pass a positive maxKeys, or replace it with a paged-capable store for that code path.
  3. If you wrote a custom Store, implement the PagedLister interface's ListPage method.
  4. Fall back to unbounded List for small/test datasets only, accepting the memory tradeoff.

Example fix

// before
page, err := objectstore.ListPage(ctx, memStore, prefix, "", 100) // Memory with maxKeys ok, but non-paged backends fail
// after
if _, ok := store.(objectstore.PagedLister); !ok {
    return fmt.Errorf("listing requires a paged backend")
}
page, err := objectstore.ListPage(ctx, store, prefix, "", 100)
Defensive patterns

Strategy: type-guard

Validate before calling

if maxKeys <= 0 { return errors.New("maxKeys must be positive") }

Type guard

func supportsPaging(s objectstore.Store) bool {
    _, ok := s.(objectstore.PagedLister)
    return ok
}

Try / catch

page, err := objectstore.ListPage(ctx, store, prefix, startAfter, maxKeys)
if errors.Is(err, objectstore.ErrPagingUnsupported) {
    return errors.New("bounded listing requires a PagedLister backend")
}

Prevention

When it happens

Trigger: Calling objectstore.ListPage with a Store that does not implement PagedLister (objectstore.go:79 type assertion fails); calling (*Memory).ListPage with maxKeys <= 0 (memory.go:84); using an in-memory or otherwise non-paged backend where the caller expects S3-style pagination.

Common situations: Swapping the object store backend from MinIO/S3 to the in-memory implementation (tests, local dev) while production code path assumes paged listing; accidentally passing zero or a negative maxKeys; adding a new custom Store implementation that forgot the cursor methods.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/348cbc72456682fd. Report an issue: GitHub.

Appendix: source

Thrown at shared/platform/objectstore/objectstore.go:39

	"time"

	"github.com/JuliusBrussee/caveman/shared/platform/env"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
)

// ErrNotFound is returned by Get/Delete when the key does not exist.
var ErrNotFound = errors.New("objectstore: object not found")

// ErrTooLarge is returned when a remote object exceeds the configured in-memory
// read ceiling. Artifact/capture payloads are bounded on write; enforcing a
// read ceiling too prevents a corrupted or attacker-written bucket object from
// exhausting a service process.
var ErrTooLarge = errors.New("objectstore: object exceeds read limit")

// ErrPagingUnsupported is returned when a caller that must bound listing
// memory is given a backend without a cursor-capable implementation.
var ErrPagingUnsupported = errors.New("objectstore: paged listing unsupported")

// Store is the blob backend contract.
type Store interface {
	// Put writes body at key, overwriting any existing object.
	Put(ctx context.Context, key string, body []byte, contentType string) error
	// Get returns the bytes at key, or ErrNotFound.
	Get(ctx context.Context, key string) ([]byte, error)
	// Delete removes key (no error if already absent).
	Delete(ctx context.Context, key string) error
	// Exists reports whether key is present.
	Exists(ctx context.Context, key string) (bool, error)
	// List returns all keys under prefix (used by retention + ZDR assertions).
	List(ctx context.Context, prefix string) ([]string, error)
}

// ObjectPage is one bounded, lexicographically ordered page of keys. NextToken
// is safe to persist as a cursor; it is empty when the prefix is exhausted.
type ObjectPage struct {

View on GitHub (pinned to 3ee70a1026)