flipped-aurora/gin-vue-admin · error

function client.ListObjectsV2() failed, err:

Error message

function client.ListObjectsV2() failed, err:

What it means

CloudflareR2.ListFiles calls ListObjectsV2 against the R2 endpoint; any SDK error from that call is wrapped in this message. The listing request was rejected before results could be read; the caller-supplied cursor is passed as ContinuationToken and can itself invalidate the call.

Source

Thrown at server/utils/upload/cloudflare_r2.go:173

	}
	bucket := global.GVA_CONFIG.CloudflareR2.Bucket

	if limit <= 0 {
		limit = 100
	}

	input := &s3.ListObjectsV2Input{
		Bucket:  aws.String(bucket),
		Prefix:  aws.String(prefix),
		MaxKeys: aws.Int32(int32(limit)),
	}
	if cursor != "" {
		input.ContinuationToken = aws.String(cursor)
	}

	out, err := client.ListObjectsV2(ctx, input)
	if err != nil {
		return nil, "", false, errors.New("function client.ListObjectsV2() failed, err:" + err.Error())
	}

	for _, obj := range out.Contents {
		fi := FileInfo{}
		if obj.Key != nil {
			fi.Key = *obj.Key
		}
		if obj.Size != nil {
			fi.Size = *obj.Size
		}
		if obj.LastModified != nil {
			fi.LastModified = *obj.LastModified
		}
		files = append(files, fi)
	}

	if out.NextContinuationToken != nil {
		nextCursor = *out.NextContinuationToken

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Verify endpoint includes your Cloudflare account ID and Bucket matches the R2 bucket name exactly
  2. Check token permissions (Object Read) and that AccessKey/SecretAccessKey are current
  3. If the cursor is stale, restart listing from empty cursor and expose only fresh nextCursor values to clients
  4. Read the wrapped err text: AccessDenied vs. InvalidToken vs. network timeout point to different fixes
  5. Confirm egress/firewall allows HTTPS to <account>.r2.cloudflarestorage.com

Example fix

// before
files, next, _, err := r2.ListFiles(ctx, prefix, cursorFromHoursAgo, 100)
// after
files, next, _, err := r2.ListFiles(ctx, prefix, "", 100) // fresh pagination
Defensive patterns

Strategy: try-catch

Validate before calling

if cfg.Endpoint == "" || !strings.Contains(cfg.Endpoint, "r2.cloudflarestorage.com") || cfg.Bucket == "" {
    return errors.New("R2 list config incomplete")
}

Type guard

null

Try / catch

files, next, hasMore, err := r2.ListFiles(ctx, prefix, cursor, 100)
if err != nil {
    logger.Error("R2 ListFiles failed", zap.Error(err))
    if strings.Contains(err.Error(), "token") || strings.Contains(err.Error(), "Denied") {
        return fmt.Errorf("check R2 credentials/permissions: %w", err)
    }
    if cursor != "" {
        return r2.ListFiles(ctx, prefix, "", 100) // stale cursor fallback
    }
    return err
}

Prevention

When it happens

Trigger: ListFiles(ctx, prefix, cursor, limit) when credentials/token are invalid or lack read permission, endpoint/bucket misconfigured, network to R2 fails, or the ContinuationToken from a previous page is expired/invalid (R2 tokens are short-lived).

Common situations: Wrong R2 endpoint (missing account ID), token rotated but old creds in config, reusing a cursor from a different bucket/prefix or after long delay, R2 outage, VPC egress blocked.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/226a2be3d15d997c. Report an issue: GitHub.