gastownhall/beads · error

failed to fetch issues since %s: %w

Error message

failed to fetch issues since %s: %w

What it means

FetchIssuesSince wraps any Execute failure with this message, including the RFC3339 `since` timestamp in the text. The wrapped cause is the underlying failure for that page fetch (GraphQL errors, rate limiting, retries exhausted, HTTP errors, auth).

Source

Thrown at internal/linear/client.go:600

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

		variables := map[string]interface{}{
			"filter": filter,
			"first":  MaxPageSize,
		}
		if cursor != "" {
			variables["after"] = cursor
		}

		req := &GraphQLRequest{
			Query:     issuesQuery,
			Variables: variables,
		}

		data, err := c.Execute(ctx, req)
		if err != nil {
			return nil, fmt.Errorf("failed to fetch issues since %s: %w", sinceStr, err)
		}

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

		allIssues = append(allIssues, issuesResp.Issues.Nodes...)

		if !issuesResp.Issues.PageInfo.HasNextPage {
			break
		}
		cursor = issuesResp.Issues.PageInfo.EndCursor
	}

	return allIssues, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap the error to see the root cause after 'since <timestamp>: '
  2. If rate-limited, wait for the reset (check X-RateLimit-Requests-Reset) and retry
  3. Verify the API token is still valid and can read the team
  4. Confirm the since timestamp is a sane RFC3339 time (not zero/epoch)
  5. Check Linear status page during widespread failures

Example fix

// before
issues, err := client.FetchIssuesSince(ctx, "open", since)
// after: retry with backoff on wrapped rate-limit errors
issues, err := client.FetchIssuesSince(ctx, "open", since)
if err != nil && strings.Contains(err.Error(), "max retries") {
    time.Sleep(rateLimitReset - time.Now().Sub(time.Time{})) // or requeue the job
    issues, err = client.FetchIssuesSince(ctx, "open", since)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if client.TeamID == "" || token == "" || since.IsZero() {
    return errors.New("linear: TEAM_ID, token, and non-zero since are required")
}

Try / catch

issues, err := client.FetchIssuesSince(ctx, "open", since)
if err != nil {
    if strings.Contains(err.Error(), "max retries") || strings.Contains(err.Error(), "rate limited") {
        // transient: requeue with the same watermark so nothing is lost
        return requeueSync(since, err)
    }
    return fmt.Errorf("sync failed permanently: %w", err)
}

Prevention

When it happens

Trigger: Execute failure inside the FetchIssuesSince pagination loop: malformed or invalid updatedAt filter (GraphQL error), expired API token, 429 retries exhausted, network failure, or 4xx/5xx HTTP response from Linear.

Common situations: Rate-limit exhaustion during a large incremental sync; a since timestamp formatted unexpectedly causing a Linear filter validation error; revoked OAuth token with a failing refresh (401 path).

Related errors


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