gastownhall/beads · error

pagination limit exceeded: stopped after %d pages

Error message

pagination limit exceeded: stopped after %d pages

What it means

FetchIssues paginated more than MaxPages pages and the X-Next-Page header kept appearing, so the library stops and returns this error to avoid an infinite loop. It indicates a malformed pagination response or an implausibly large result set.

Source

Thrown at internal/gitlab/client.go:273

		}

		var issues []Issue
		if err := json.Unmarshal(respBody, &issues); err != nil {
			return nil, fmt.Errorf("failed to parse issues response: %w", err)
		}

		allIssues = append(allIssues, issues...)

		// Check for next page
		nextPage := headers.Get("X-Next-Page")
		if nextPage == "" {
			break
		}
		page++

		// Guard against infinite pagination loops from malformed responses
		if page > MaxPages {
			return nil, fmt.Errorf("pagination limit exceeded: stopped after %d pages", MaxPages)
		}
	}

	return filterByProject(allIssues, filter), nil
}

// FetchIssuesSince retrieves issues from GitLab that have been updated since the given time.
// This enables incremental sync by only fetching issues modified after the last sync.
func (c *Client) FetchIssuesSince(ctx context.Context, state string, since time.Time, filters ...*IssueFilter) ([]Issue, error) {
	var filter *IssueFilter
	if len(filters) > 0 {
		filter = filters[0]
	}

	var allIssues []Issue
	page := 1

	sinceStr := since.UTC().Format(time.RFC3339)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the server honors per_page by checking X-Total-Pages/X-Next-Page on a manual curl
  2. Check whether a proxy or mock is replaying the X-Next-Page header
  3. Increase MaxPages in the library if the project legitimately exceeds it
  4. Use FetchIssuesSince for incremental sync to reduce pages fetched

Example fix

// before
client := gitlab.NewClient(token, gitlab.WithMaxPages(10)) // too small for huge project
// after
client := gitlab.NewClient(token, gitlab.WithMaxPages(100)) // or use FetchIssuesSince
Defensive patterns

Strategy: validation

Validate before calling

resp, _ := http.Get(baseURL + "/api/v4/projects/" + proj + "/issues?per_page=100")
total := resp.Header.Get("X-Total-Pages")
n, _ := strconv.Atoi(total)
if n > maxPagesConfigured { return fmt.Errorf("project needs %d pages > MaxPages %d", n, maxPagesConfigured) }

Type guard

func isPaginationLimitErr(err error) bool {
	return strings.Contains(err.Error(), "pagination limit exceeded")
}

Try / catch

issues, err := client.FetchIssues(ctx, "opened")
if err != nil {
	if strings.Contains(err.Error(), "pagination limit exceeded") {
		// switch to incremental sync or raise MaxPages
	}
}

Prevention

When it happens

Trigger: The GitLab API (or an interceptor) keeps returning a non-empty X-Next-Page header for more than MaxPages consecutive pages during FetchIssues, e.g. a proxy replaying headers or per_page being ignored.

Common situations: Corporate proxies stripping per_page or echoing X-Next-Page, mocking servers that always set X-Next-Page, or extremely large projects exceeding MaxPages with a small MaxPageSize.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/8b4619b3fdeb12d7. Report an issue: GitHub.