gastownhall/beads · critical

fetch child %s: %w

Error message

fetch child %s: %w

What it means

During ReconcileParents, fetching a child issue failed with a rate-limit-exhausted error, tripping the circuit breaker. Instead of continuing to hammer the API for every remaining link, the loop aborts immediately and returns this wrapped error alongside partial stats.

Source

Thrown at internal/linear/parent_reconcile.go:180

			return entry{}, err
		}
		e := entry{issue: issue, client: client}
		// Cache nil too — repeated lookups are still cheap.
		fetched[identifier] = e
		return e, nil
	}

	for _, link := range links {
		if link.ChildIdentifier == "" || link.ParentIdentifier == "" {
			continue
		}

		childE, err := fetchIssue(link.ChildIdentifier)
		if err != nil {
			// Rate-limit circuit breaker tripped — stop now rather than
			// hammer the API for every remaining link.
			if isRateLimitExhausted(err) {
				return stats, fmt.Errorf("fetch child %s: %w", link.ChildIdentifier, err)
			}
			stats.Errors = append(stats.Errors,
				fmt.Errorf("fetch child %s: %w", link.ChildIdentifier, err))
			continue
		}
		if childE.issue == nil {
			stats.NotFound = append(stats.NotFound, link.ChildIdentifier)
			continue
		}

		parentE, err := fetchIssue(link.ParentIdentifier)
		if err != nil {
			if isRateLimitExhausted(err) {
				return stats, fmt.Errorf("fetch parent %s: %w", link.ParentIdentifier, err)
			}
			stats.Errors = append(stats.Errors,
				fmt.Errorf("fetch parent %s: %w", link.ParentIdentifier, err))
			continue

View on GitHub (pinned to 71377f2769)

Solutions

  1. Wait for the rate-limit window to reset, then re-run ReconcileParents (it resumes on remaining links).
  2. Reduce concurrency/batch size or spread reconciliation across intervals.
  3. Use a dedicated API token for reconciliation so other workloads don't drain the quota.
  4. Honor any Retry-After guidance and add longer backoff before re-running.
Defensive patterns

Strategy: retry

Try / catch

stats, err := linear.ReconcileParents(ctx, deps)
if err != nil && isRateLimitExhausted(err) {
    time.Sleep(rateLimitResetWindow)
    stats, err = linear.ReconcileParents(ctx, deps)
}

Prevention

When it happens

Trigger: fetchIssue(link.ChildIdentifier) returns an error for which isRateLimitExhausted(err) is true while reconciling parent links in ReconcileParents.

Common situations: Large backlogs of unlinked issues pushed through reconciliation in a short window; shared API token exhausted by other integrations; missing backoff between reconciliation runs.

Related errors


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