From 2d610abc654efd0bc155db36f3cfba2a79d7e13d Mon Sep 17 00:00:00 2001 From: "c1-dev-bot[bot]" <2740113+c1-dev-bot[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 20:11:50 +0000 Subject: [PATCH] Paginate Grants() responses to avoid gRPC ResourceExhausted errors The Grants() function returned all grants for a resource in a single gRPC response. When the grant count is large (e.g. 77,000 direct grants), the serialized response exceeds the baton-sdk's default 4MB MaxRecvMsgSize limit, causing ResourceExhausted. Add offset-based pagination with a default page size of 1000 grants. The SDK's requested page size is respected when provided. This follows the established connector-side pagination pattern from CXH-1255. Fixes: CXH-1552 --- pkg/connector/resources.go | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/pkg/connector/resources.go b/pkg/connector/resources.go index 57b63ec9..f32a9107 100644 --- a/pkg/connector/resources.go +++ b/pkg/connector/resources.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "sort" + "strconv" "strings" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" @@ -14,6 +15,8 @@ import ( "go.uber.org/zap" ) +const defaultGrantPageSize = 1000 + type resourceBuilder struct { cache *syncCache resourceType *v2.ResourceType @@ -156,7 +159,35 @@ func (b *resourceBuilder) Grants(ctx context.Context, resource *v2.Resource, return allGrants[i].GetEntitlement().GetId() < allGrants[j].GetEntitlement().GetId() }) - return allGrants, &rs.SyncOpResults{}, nil + pageSize := defaultGrantPageSize + if opts.PageToken.Size > 0 { + pageSize = opts.PageToken.Size + } + + offset := 0 + if opts.PageToken.Token != "" { + var err error + offset, err = strconv.Atoi(opts.PageToken.Token) + if err != nil { + return nil, nil, fmt.Errorf("baton-file: invalid grants page token: %w", err) + } + } + + if offset > len(allGrants) { + offset = len(allGrants) + } + + end := offset + pageSize + if end > len(allGrants) { + end = len(allGrants) + } + + var nextPageToken string + if end < len(allGrants) { + nextPageToken = strconv.Itoa(end) + } + + return allGrants[offset:end], &rs.SyncOpResults{NextPageToken: nextPageToken}, nil } func buildUserResource(ctx context.Context, userData client.UserData,