{"record":{"id":"aef7a8e5e1e373ea","repo":"gastownhall/beads","slug":"failed-to-create-haiku-client-w","errorCode":null,"errorMessage":"failed to create Haiku client: %w","messagePattern":"failed to create Haiku client: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/compact/compactor.go","lineNumber":71,"sourceCode":"\t\t\tConcurrency: defaultConcurrency,\n\t\t}\n\t}\n\tif config.Concurrency <= 0 {\n\t\tconfig.Concurrency = defaultConcurrency\n\t}\n\tif apiKey != \"\" {\n\t\tconfig.APIKey = apiKey\n\t}\n\n\tvar haiClient summarizer\n\tvar err error\n\tif !config.DryRun {\n\t\thaiClient, err = newHaikuClient(config.APIKey)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, errAPIKeyRequired) {\n\t\t\t\tconfig.DryRun = true\n\t\t\t} else {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to create Haiku client: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\tif hc, ok := haiClient.(*haikuClient); ok && hc != nil {\n\t\thc.auditEnabled = config.AuditEnabled\n\t\thc.auditActor = config.Actor\n\t}\n\n\treturn &Compactor{\n\t\tstore:      store,\n\t\tsummarizer: haiClient,\n\t\tconfig:     config,\n\t}, nil\n}\n\n// CompactTier1 compacts a single issue at Tier 1 (basic summarization).\nfunc (c *Compactor) CompactTier1(ctx context.Context, issueID string) error {\n\tif ctx.Err() != nil {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/compact/compactor.go#L53-L89","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Set DryRun: true in the Config (or pass an empty apiKey) if you do not intend to use the real AI path","Inspect the wrapped error (%w) to find the root cause from newHaikuClient and fix the API key value","Validate the API key format/credentials before calling compact.New","In tests, always pass DryRun: true unless the test explicitly exercises the API path"],"exampleFix":"// before\nc, err := compact.New(store, os.Getenv(\"ANTHROPIC_API_KEY\"), nil)\n// after\nkey := strings.TrimSpace(os.Getenv(\"ANTHROPIC_API_KEY\"))\ncfg := &compact.Config{DryRun: key == \"\"}\nc, err := compact.New(store, key, cfg)","handlingStrategy":"validation","validationCode":"key := strings.TrimSpace(os.Getenv(\"ANTHROPIC_API_KEY\"))\nif key == \"\" {\n    cfg.DryRun = true // missing key downgrades to dry-run, not an error\n} else if len(key) < 20 || !strings.HasPrefix(key, \"sk-\") {\n    return fmt.Errorf(\"ANTHROPIC_API_KEY looks malformed\")\n}","typeGuard":null,"tryCatchPattern":"c, err := compact.New(store, key, cfg)\nif err != nil {\n    var retryable bool\n    if !strings.Contains(err.Error(), \"API key\") { retryable = true }\n    return fmt.Errorf(\"compactor init: %w (retryable=%v)\", err, retryable)\n}","preventionTips":["Always set DryRun: true in tests and CI without secrets","Trim and validate the API key before passing it to New","Remember an empty key silently downgrades to dry-run — log that downgrade","Check New's error immediately instead of using a possibly-nil Compactor"],"tags":["go","configuration","api-key","ai-client"],"backgroundTag":"invalid-api-key","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}