{"record":{"id":"c7a78be1173fb210","repo":"gastownhall/beads","slug":"reopen-id-must-not-be-empty","errorCode":null,"errorMessage":"reopen: id must not be empty","messagePattern":"reopen: id must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue.go","lineNumber":1712,"sourceCode":"\t\treturn CloseIssueResult{}, fmt.Errorf(\"close %s: reload: %w\", id, err)\n\t}\n\treturn CloseIssueResult{\n\t\tIssue:  issue,\n\t\tClosed: !row.AlreadyClosed,\n\t}, nil\n}\n\nfunc (u *issueUseCaseImpl) ReopenIssue(ctx context.Context, id string, params ReopenIssueParams, actor string) (ReopenIssueResult, error) {\n\treturn u.reopen(ctx, id, params, actor, false)\n}\n\nfunc (u *issueUseCaseImpl) ReopenWisp(ctx context.Context, id string, params ReopenIssueParams, actor string) (ReopenIssueResult, error) {\n\treturn u.reopen(ctx, id, params, actor, true)\n}\n\nfunc (u *issueUseCaseImpl) reopen(ctx context.Context, id string, params ReopenIssueParams, actor string, useWisp bool) (ReopenIssueResult, error) {\n\tif id == \"\" {\n\t\treturn ReopenIssueResult{}, fmt.Errorf(\"reopen: id must not be empty\")\n\t}\n\tif actor == \"\" {\n\t\treturn ReopenIssueResult{}, fmt.Errorf(\"reopen: actor must not be empty\")\n\t}\n\trow, err := u.issueRepo.Reopen(ctx, id, ReopenRowParams{Reason: params.Reason}, actor, IssueTableOpts{UseWispsTable: useWisp})\n\tif err != nil {\n\t\treturn ReopenIssueResult{}, fmt.Errorf(\"reopen %s: %w\", id, err)\n\t}\n\tissue, err := u.issueRepo.Get(ctx, id, IssueTableOpts{UseWispsTable: row.IsWisp})\n\tif err != nil {\n\t\treturn ReopenIssueResult{}, fmt.Errorf(\"reopen %s: reload: %w\", id, err)\n\t}\n\treturn ReopenIssueResult{\n\t\tIssue:    issue,\n\t\tReopened: row.Updated,\n\t}, nil\n}\n","sourceCodeStart":1694,"sourceCodeEnd":1730,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue.go#L1694-L1730","documentation":"A guard error thrown by the reopen() use case (backing ReopenIssue and ReopenWisp) when the issue ID argument is empty. Like the close path, the library fails fast because an empty ID can never identify a row to reopen.","triggerScenarios":"Calling ReopenIssue / ReopenWisp with id=\"\" — typically an unset variable, empty CLI flag, or blank field from a parsed input passed straight through to the use case.","commonSituations":"Scripts reopening issues from a list where one entry had an empty ID; misconfigured automation not resolving the issue reference; copy/paste of an invocation with the ID argument dropped.","solutions":["Supply a valid issue ID before invoking ReopenIssue.","Filter empty IDs out of batch inputs before looping reopen calls.","Validate id != \"\" at the boundary (CLI/parser) and emit a clear user-facing message."],"exampleFix":"// before\nusecase.ReopenIssue(ctx, id, params, actor) // id == \"\"\n// after\nif id == \"\" { return fmt.Errorf(\"reopen: issue id required\") }\nusecase.ReopenIssue(ctx, id, params, actor)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(id) == \"\" {\n    return fmt.Errorf(\"reopen: issue id must not be empty\")\n}","typeGuard":"func validIssueID(id string) bool { return strings.TrimSpace(id) != \"\" }","tryCatchPattern":"if err := run(); err != nil {\n    if strings.Contains(err.Error(), \"reopen: id must not be empty\") {\n        return fmt.Errorf(\"reopen requires an issue ID\")\n    }\n    return err\n}","preventionTips":["Validate IDs at the CLI/parser boundary.","Skip empty IDs in batch reopen loops with a warning.","Use constructors that reject empty identifiers.","Fail fast with a clear message before touching storage."],"tags":["validation","reopen-issue","empty-id","guard"],"backgroundTag":"empty-required-argument","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}