gastownhall/beads · error

failed to create Haiku client: %w

Error message

failed to create Haiku client: %w

What it means

New() builds an Anthropic Haiku client for AI summarization unless DryRun is set. When newHaikuClient fails with an error other than errAPIKeyRequired (which silently downgrades to dry-run), New aborts and wraps the underlying failure. This means the API key was present but the client could not be constructed.

Source

Thrown at internal/compact/compactor.go:71

			Concurrency: defaultConcurrency,
		}
	}
	if config.Concurrency <= 0 {
		config.Concurrency = defaultConcurrency
	}
	if apiKey != "" {
		config.APIKey = apiKey
	}

	var haiClient summarizer
	var err error
	if !config.DryRun {
		haiClient, err = newHaikuClient(config.APIKey)
		if err != nil {
			if errors.Is(err, errAPIKeyRequired) {
				config.DryRun = true
			} else {
				return nil, fmt.Errorf("failed to create Haiku client: %w", err)
			}
		}
	}
	if hc, ok := haiClient.(*haikuClient); ok && hc != nil {
		hc.auditEnabled = config.AuditEnabled
		hc.auditActor = config.Actor
	}

	return &Compactor{
		store:      store,
		summarizer: haiClient,
		config:     config,
	}, nil
}

// CompactTier1 compacts a single issue at Tier 1 (basic summarization).
func (c *Compactor) CompactTier1(ctx context.Context, issueID string) error {
	if ctx.Err() != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Set DryRun: true in the Config (or pass an empty apiKey) if you do not intend to use the real AI path
  2. Inspect the wrapped error (%w) to find the root cause from newHaikuClient and fix the API key value
  3. Validate the API key format/credentials before calling compact.New
  4. In tests, always pass DryRun: true unless the test explicitly exercises the API path

Example fix

// before
c, err := compact.New(store, os.Getenv("ANTHROPIC_API_KEY"), nil)
// after
key := strings.TrimSpace(os.Getenv("ANTHROPIC_API_KEY"))
cfg := &compact.Config{DryRun: key == ""}
c, err := compact.New(store, key, cfg)
Defensive patterns

Strategy: validation

Validate before calling

key := strings.TrimSpace(os.Getenv("ANTHROPIC_API_KEY"))
if key == "" {
    cfg.DryRun = true // missing key downgrades to dry-run, not an error
} else if len(key) < 20 || !strings.HasPrefix(key, "sk-") {
    return fmt.Errorf("ANTHROPIC_API_KEY looks malformed")
}

Try / catch

c, err := compact.New(store, key, cfg)
if err != nil {
    var retryable bool
    if !strings.Contains(err.Error(), "API key") { retryable = true }
    return fmt.Errorf("compactor init: %w (retryable=%v)", err, retryable)
}

Prevention

When it happens

Trigger: Calling compact.New(store, apiKey, &compact.Config{DryRun:false, ...}) with a non-empty API key that newHaikuClient rejects for reasons other than being missing (e.g. malformed/invalid key format, client construction failure).

Common situations: API key pasted with whitespace/newline or wrong env var containing a garbage value; an Anthropic SDK client constructor failing on bad configuration; tests constructing a real client unintentionally because DryRun was left false and a stub key was supplied.

Related errors


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