gastownhall/beads · error

pagination limit exceeded: stopped after %d pages

Error message

pagination limit exceeded: stopped after %d pages

What it means

SearchIssues aborts when pagination exceeds MaxPages iterations, returning nil results. The library enforces this as a runaway-loop guard so a JQL query whose pagination never terminates (or whose result set is enormous) cannot hang the caller forever. It is a deliberate safety limit, not a server error.

Source

Thrown at internal/jira/client.go:183

// SearchIssues queries Jira using JQL and returns all matching issues, handling pagination.
func (c *Client) SearchIssues(ctx context.Context, jql string) ([]Issue, error) {
	var allIssues []Issue
	startAt := 0
	nextPageToken := ""
	maxResults := 100
	page := 0
	useV2Pagination := c.APIVersion == "2"

	for {
		select {
		case <-ctx.Done():
			return allIssues, ctx.Err()
		default:
		}

		page++
		if page > MaxPages {
			return nil, fmt.Errorf("pagination limit exceeded: stopped after %d pages", MaxPages)
		}

		params := url.Values{
			"jql":        {jql},
			"fields":     {searchFields},
			"maxResults": {fmt.Sprintf("%d", maxResults)},
		}
		if useV2Pagination {
			params.Set("startAt", fmt.Sprintf("%d", startAt))
		} else if nextPageToken != "" {
			params.Set("nextPageToken", nextPageToken)
		}

		// v3 uses /search/jql; v2 uses /search (both accept jql as a query param)
		searchPath := "search/jql"
		if useV2Pagination {
			searchPath = "search"
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Narrow the JQL: add time bounds (updated >= -7d), project, or component filters to reduce result count.
  2. Increase MaxPages if the limit is legitimately too low for your dataset.
  3. Check that c.APIVersion matches your deployment so the correct pagination mode (startAt vs nextPageToken) is used.
  4. Process results in batches with multiple targeted searches instead of one giant query.

Example fix

// before
issues, err := client.SearchIssues(ctx, "project = BIGPROJECT")
// after: bound the query window
issues, err := client.SearchIssues(ctx, "project = BIGPROJECT AND updated >= -30d ORDER BY updated ASC")
Defensive patterns

Strategy: validation

Validate before calling

// Estimate result size before searching:
total, err := countIssues(client, jql) // run jql with maxResults=0 to read Total
if total > 5000 {
    return fmt.Errorf("JQL matches %d issues; narrow the query or batch by time window", total)
}

Type guard

func isPaginationLimit(err error) bool {
    return err != nil && strings.Contains(err.Error(), "pagination limit exceeded")
}

Try / catch

issues, err := client.SearchIssues(ctx, jql)
if isPaginationLimit(err) {
    // fall back to time-sliced queries
    for _, window := range timeWindows(30 * 24 * time.Hour) {
        slice, e := client.SearchIssues(ctx, jql+" AND updated >= "+window[0]+" AND updated <= "+window[1])
        if e != nil { return e }
        issues = append(issues, slice...)
    }
    return nil
}

Prevention

When it happens

Trigger: Calling SearchIssues with a JQL matching more issues than MaxPages * maxResults (100 per page), or a server whose pagination signal (Total / nextPageToken / isLast) never indicates completion.

Common situations: Overly broad JQL like project = X order by updated on a huge instance; a broken next-page token loop on Jira Cloud; API version mismatch (v2/v3 pagination semantics) causing the termination check never to fire.

Related errors


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