gastownhall/beads · error
db: MovePersistence %s: get issue: %w
Error message
db: MovePersistence %s: get issue: %w
What it means
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.
Source
Thrown at internal/storage/domain/db/issue.go:126
// PromoteFromEphemeral promotes an active wisp into the Dolt-versioned issues
// plane in place: same id, wisp_type retained, labels/dependencies/events/
// comments carried across to the permanent tables, inbound wisp-targeted
// dependency edges retargeted, and blocked state recomputed. It delegates to
// the exact issueops implementation the classic (direct/embedded) route runs,
// so the two modes cannot drift. The issueops error is returned unwrapped on
// purpose: the CLI surfaces it verbatim ("wisp <id> not found"), and that
// text is part of the classic error contract.
func (r *issueSQLRepositoryImpl) PromoteFromEphemeral(ctx context.Context, id, actor string) error {
if id == "" {
return errors.New("db: PromoteFromEphemeral: id must not be empty")
}
return issueops.PromoteFromEphemeralInTx(ctx, r.runner, id, actor)
}
func (r *issueSQLRepositoryImpl) MovePersistence(ctx context.Context, id string, mode types.PersistenceMode, actor string) (bool, error) {
issue, err := issueops.GetIssueInTx(ctx, r.runner, id)
if err != nil {
return false, fmt.Errorf("db: MovePersistence %s: get issue: %w", id, err)
}
result, err := issueops.MoveIssuePersistenceInTx(ctx, r.runner, issue, mode, actor)
if err != nil {
return false, fmt.Errorf("db: MovePersistence %s: %w", id, err)
}
return result.Changed, nil
}
func (r *issueSQLRepositoryImpl) Update(ctx context.Context, id string, updates map[string]any, actor string, opts domain.IssueTableOpts) error {
if id == "" {
return errors.New("db: Update: id must not be empty")
}
if len(updates) == 0 {
return nil
}
updates = cloneUpdateFields(updates)
// Pop the close-policy override before anything reads the map as a set of
// columns, mirroring issueops.updateIssueInTx. The no-op filter below keepsView on GitHub (pinned to 71377f2769)
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
Example fix
// before
changed, err := repo.MovePersistence(ctx, id, mode, actor)
// after
if _, err := repo.Get(ctx, id, domain.IssueTableOpts{}); err != nil {
return fmt.Errorf("issue %s not found: %w", id, err)
}
changed, err := repo.MovePersistence(ctx, id, mode, actor) Defensive patterns
Strategy: validation
Validate before calling
if id == "" { return fmt.Errorf("issue ID required") }
if !strings.Contains(id, "-") { return fmt.Errorf("malformed issue ID %q", id) }
if _, err := repo.Get(ctx, id, opts); err != nil { return fmt.Errorf("issue %s not found", id) } Type guard
func isNotFoundErr(err error) bool { return errors.Is(err, sql.ErrNoRows) } Try / catch
changed, err := repo.MovePersistence(ctx, id, mode, actor)
if err != nil {
if isNotFoundErr(err) { return ErrIssueNotFound }
return fmt.Errorf("move persistence for %s: %w", id, err)
} Prevention
- Validate issue IDs before persistence operations
- Refresh cached IDs after concurrent mutations
- Ensure single-writer discipline per issue
When it happens
Trigger: 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.
Common situations: Caller passes an issue ID deleted by another process mid-workflow; ID typo or wrong-prefix ID; database unreachable mid-transaction.
Related errors
- db: MovePersistence %s: %w
- failed to update gate with discovered run ID: %w
- failed to migrate credential keys: %w
- failed to scan peer for migration: %w
- failed to iterate peers for migration: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/73d63e69f429e5f2.
Report an issue: GitHub.