gastownhall/beads · error

failed to fetch issues since

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).

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.

Appendix: 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)