{"record":{"id":"a97f3c23d8cccf0b","repo":"gastownhall/beads","slug":"close-s-w","errorCode":null,"errorMessage":"close %s: %w","messagePattern":"close (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue.go","lineNumber":1672,"sourceCode":"func (u *issueUseCaseImpl) CloseIssueChecked(ctx context.Context, id string, params CloseIssueParams, actor string, force bool) (CloseIssueResult, error) {\n\treturn u.closeChecked(ctx, id, params, actor, force, false)\n}\n\n// CloseWispChecked is the wisp twin of CloseIssueChecked.\nfunc (u *issueUseCaseImpl) CloseWispChecked(ctx context.Context, id string, params CloseIssueParams, actor string, force bool) (CloseIssueResult, error) {\n\treturn u.closeChecked(ctx, id, params, actor, force, true)\n}\n\nfunc (u *issueUseCaseImpl) closeChecked(ctx context.Context, id string, params CloseIssueParams, actor string, force, useWisp bool) (CloseIssueResult, error) {\n\tif id == \"\" {\n\t\treturn CloseIssueResult{}, fmt.Errorf(\"close: id must not be empty\")\n\t}\n\tif actor == \"\" {\n\t\treturn CloseIssueResult{}, fmt.Errorf(\"close: actor must not be empty\")\n\t}\n\trow, err := u.issueRepo.CloseChecked(ctx, id, CloseRowParams{Reason: params.Reason, Session: params.Session}, actor, force)\n\tif err != nil {\n\t\treturn CloseIssueResult{}, fmt.Errorf(\"close %s: %w\", id, err)\n\t}\n\tissue, err := u.issueRepo.Get(ctx, id, IssueTableOpts{UseWispsTable: row.IsWisp || useWisp})\n\tif err != nil {\n\t\treturn CloseIssueResult{}, fmt.Errorf(\"close %s: reload: %w\", id, err)\n\t}\n\treturn CloseIssueResult{Issue: issue, Closed: !row.AlreadyClosed, OpenChildren: row.OpenChildren}, nil\n}\n\nfunc (u *issueUseCaseImpl) close(ctx context.Context, id string, params CloseIssueParams, actor string, useWisp bool) (CloseIssueResult, error) {\n\tif id == \"\" {\n\t\treturn CloseIssueResult{}, fmt.Errorf(\"close: id must not be empty\")\n\t}\n\tif actor == \"\" {\n\t\treturn CloseIssueResult{}, fmt.Errorf(\"close: actor must not be empty\")\n\t}\n\trow, err := u.issueRepo.Close(ctx, id, CloseRowParams{Reason: params.Reason, Session: params.Session}, actor, IssueTableOpts{UseWispsTable: useWisp})\n\tif err != nil {\n\t\treturn CloseIssueResult{}, fmt.Errorf(\"close %s: %w\", id, err)","sourceCodeStart":1654,"sourceCodeEnd":1690,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue.go#L1654-L1690","documentation":"Wraps the underlying error from issueRepo.CloseChecked — the actual close-write against the issues/wisps table failed. The ID is interpolated for context and the storage error is chained as %w. This is the generic wrapper for storage-level close failures, distinct from the empty-id/actor guards and from the later reload failure.","triggerScenarios":"CloseChecked call fails: issue not found in either table, already closed without force, DB write error, permission/driver error, context canceled during the write.","commonSituations":"Typo in issue ID (bd-9999 doesn't exist); trying to close an already-closed issue without force; database locked or offline; concurrent sync conflict.","solutions":["Read the wrapped %w error: if not-found, verify the ID exists (bd show <id>).","If already-closed, either treat as success in your flow or pass force=true.","Check DB connectivity/locks and retry transient errors.","Use the matching table variant — a wisp ID may need CloseWisp* rather than CloseIssue* depending on your lookup path."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-check existence and state before closing\nissue, err := uc.GetIssue(ctx, id)\nif err != nil {\n    return fmt.Errorf(\"issue %s not found: %w\", id, err)\n}\nif issue.Status == StatusClosed && !force {\n    return fmt.Errorf(\"issue %s already closed; pass force to no-op\", id)\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n    switch {\n    case errors.Is(err, ErrNotFound):\n        return fmt.Errorf(\"close %s: no such issue; verify ID with bd show\", id)\n    case errors.Is(err, ErrAlreadyClosed):\n        return nil // idempotent close\n    case isTransientStorageErr(err):\n        return retryWithBackoff(closeOp)\n    default:\n        return fmt.Errorf(\"close %s: %w\", id, err)\n    }\n}","preventionTips":["Verify issue IDs exist (bd show) before closing in automation.","Design closes as idempotent — treat already-closed as success or use force.","Check DB health (bd doctor) before bulk close operations.","Use short, retriable context timeouts around close writes."],"tags":["close","storage","database"],"backgroundTag":"close-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}