gastownhall/beads · error

failed to summarize: %w

Error message

failed to summarize: %w

What it means

CompactTier1 calls the summarizer's SummarizeTier1 to produce the AI summary. Any failure inside the summarization call (Haiku API error, network failure, rate limit, bad response) is aborted and wrapped with this message. The issue's content is untouched at this point.

Source

Thrown at internal/compact/compactor.go:120

		return fmt.Errorf("issue %s is not eligible for Tier 1 compaction", issueID)
	}

	issue, err := c.store.GetIssue(ctx, issueID)
	if err != nil {
		return fmt.Errorf("failed to fetch issue: %w", err)
	}

	// Calculate original size
	originalSize := len(issue.Description) + len(issue.Design) + len(issue.Notes) + len(issue.AcceptanceCriteria)

	if c.config.DryRun {
		return fmt.Errorf("dry-run: would compact %s (original size: %d bytes)", issueID, originalSize)
	}

	// Get summary from AI
	summary, err := c.summarizer.SummarizeTier1(ctx, issue)
	if err != nil {
		return fmt.Errorf("failed to summarize: %w", err)
	}

	// Check if compaction would actually reduce size
	compactedSize := len(summary)
	if compactedSize >= originalSize {
		warningMsg := fmt.Sprintf("Tier 1 compaction skipped: summary (%d bytes) not shorter than original (%d bytes)", compactedSize, originalSize)
		if err := c.store.AddComment(ctx, issueID, "compactor", warningMsg); err != nil {
			return fmt.Errorf("failed to record warning: %w", err)
		}
		return fmt.Errorf("compaction would increase size (%d → %d bytes), keeping original", originalSize, compactedSize)
	}

	// Archive the original content BEFORE the destructive overwrite, so the
	// compaction is reversible (bd restore reads this snapshot). If archiving
	// fails we abort with the original content intact rather than lose it.
	if err := c.store.SnapshotIssue(ctx, issueID, 1); err != nil {
		return fmt.Errorf("failed to archive pre-compaction snapshot: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error for the specific API/network failure
  2. Verify ANTHROPIC_API_KEY validity and remaining quota
  3. Retry with backoff — API failures are often transient
  4. Check context timeouts are generous enough for large issues; consider pausing batch runs on repeated 429s

Example fix

// before
if err := c.CompactTier1(ctx, id); err != nil { return err }
// after
err := c.CompactTier1(ctx, id)
if err != nil && strings.Contains(err.Error(), "failed to summarize") {
    time.Sleep(backoff)
    err = c.CompactTier1(ctx, id)
}
return err
Defensive patterns

Strategy: retry

Validate before calling

if os.Getenv("ANTHROPIC_API_KEY") == "" { return fmt.Errorf("no API key") }
if err := ctx.Err(); err != nil { return err }

Try / catch

err := c.CompactTier1(ctx, id)
if err != nil && strings.Contains(err.Error(), "failed to summarize") {
    select {
    case <-time.After(exponentialBackoff(attempt)):
        err = c.CompactTier1(ctx, id)
    case <-ctx.Done():
        return ctx.Err()
    }
}
return err

Prevention

When it happens

Trigger: c.summarizer.SummarizeTier1(ctx, issue) returns a non-nil error — e.g. Anthropic API rejected the key (401), rate limit hit (429), request timeout/context cancelled, network unreachable, or the model returned an unusable response.

Common situations: Expired or revoked Anthropic API key; quota exhaustion during large batch compactions; transient network outage; context deadline too short for large issues; API downtime.

Related errors


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