{"record":{"id":"73d63e69f429e5f2","repo":"gastownhall/beads","slug":"db-movepersistence-s-get-issue-w","errorCode":null,"errorMessage":"db: MovePersistence %s: get issue: %w","messagePattern":"db: MovePersistence (.+?): get issue: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue.go","lineNumber":126,"sourceCode":"// PromoteFromEphemeral promotes an active wisp into the Dolt-versioned issues\n// plane in place: same id, wisp_type retained, labels/dependencies/events/\n// comments carried across to the permanent tables, inbound wisp-targeted\n// dependency edges retargeted, and blocked state recomputed. It delegates to\n// the exact issueops implementation the classic (direct/embedded) route runs,\n// so the two modes cannot drift. The issueops error is returned unwrapped on\n// purpose: the CLI surfaces it verbatim (\"wisp <id> not found\"), and that\n// text is part of the classic error contract.\nfunc (r *issueSQLRepositoryImpl) PromoteFromEphemeral(ctx context.Context, id, actor string) error {\n\tif id == \"\" {\n\t\treturn errors.New(\"db: PromoteFromEphemeral: id must not be empty\")\n\t}\n\treturn issueops.PromoteFromEphemeralInTx(ctx, r.runner, id, actor)\n}\n\nfunc (r *issueSQLRepositoryImpl) MovePersistence(ctx context.Context, id string, mode types.PersistenceMode, actor string) (bool, error) {\n\tissue, err := issueops.GetIssueInTx(ctx, r.runner, id)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"db: MovePersistence %s: get issue: %w\", id, err)\n\t}\n\tresult, err := issueops.MoveIssuePersistenceInTx(ctx, r.runner, issue, mode, actor)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"db: MovePersistence %s: %w\", id, err)\n\t}\n\treturn result.Changed, nil\n}\n\nfunc (r *issueSQLRepositoryImpl) Update(ctx context.Context, id string, updates map[string]any, actor string, opts domain.IssueTableOpts) error {\n\tif id == \"\" {\n\t\treturn errors.New(\"db: Update: id must not be empty\")\n\t}\n\tif len(updates) == 0 {\n\t\treturn nil\n\t}\n\tupdates = cloneUpdateFields(updates)\n\t// Pop the close-policy override before anything reads the map as a set of\n\t// columns, mirroring issueops.updateIssueInTx. The no-op filter below keeps","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue.go#L108-L144","documentation":"Wraps failure to fetch the issue row during MovePersistence, which migrates an issue between ephemeral and persistent storage. The error comes from issueops.GetIssueInTx; this wrapper adds the issue ID and the 'get issue' stage for diagnosis. No issue found or any select failure lands here.","triggerScenarios":"Calling MovePersistence with an ID that doesn't exist (sql.ErrNoRows propagated through GetIssueInTx), a corrupted row that fails scanning, or a transaction/connection error during the select.","commonSituations":"Caller passes an issue ID deleted by another process mid-workflow; ID typo or wrong-prefix ID; database unreachable mid-transaction.","solutions":["Verify the issue ID exists (bd show <id>) before moving persistence","Check the wrapped error: sql.ErrNoRows means the ID is absent, other errors are infrastructure","Retry on transient connection errors","If IDs come from user input, validate the format before the call"],"exampleFix":"// before\nchanged, err := repo.MovePersistence(ctx, id, mode, actor)\n// after\nif _, err := repo.Get(ctx, id, domain.IssueTableOpts{}); err != nil {\n    return fmt.Errorf(\"issue %s not found: %w\", id, err)\n}\nchanged, err := repo.MovePersistence(ctx, id, mode, actor)","handlingStrategy":"validation","validationCode":"if id == \"\" { return fmt.Errorf(\"issue ID required\") }\nif !strings.Contains(id, \"-\") { return fmt.Errorf(\"malformed issue ID %q\", id) }\nif _, err := repo.Get(ctx, id, opts); err != nil { return fmt.Errorf(\"issue %s not found\", id) }","typeGuard":"func isNotFoundErr(err error) bool { return errors.Is(err, sql.ErrNoRows) }","tryCatchPattern":"changed, err := repo.MovePersistence(ctx, id, mode, actor)\nif err != nil {\n    if isNotFoundErr(err) { return ErrIssueNotFound }\n    return fmt.Errorf(\"move persistence for %s: %w\", id, err)\n}","preventionTips":["Validate issue IDs before persistence operations","Refresh cached IDs after concurrent mutations","Ensure single-writer discipline per issue"],"tags":["database","issue","persistence","migration"],"backgroundTag":"issue-not-found","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}