gastownhall/beads · error

rewrite refs %s: %w

Error message

rewrite refs %s: %w

What it means

Wraps a failure from issueRepo.Update while rewriting textual references to a deleted issue inside a connected issue's description/notes/design/acceptance_criteria. After deletion, references like `bd-123` are replaced with `[deleted:bd-123]`; if the row update fails, the whole deleteMany aborts with this error naming the failing connected issue ID.

Source

Thrown at internal/storage/domain/issue_delete.go:425

			updates := map[string]any{}
			if re.MatchString(conn.Description) {
				updates["description"] = re.ReplaceAllString(conn.Description, replacement)
			}
			if conn.Notes != "" && re.MatchString(conn.Notes) {
				updates["notes"] = re.ReplaceAllString(conn.Notes, replacement)
			}
			if conn.Design != "" && re.MatchString(conn.Design) {
				updates["design"] = re.ReplaceAllString(conn.Design, replacement)
			}
			if conn.AcceptanceCriteria != "" && re.MatchString(conn.AcceptanceCriteria) {
				updates["acceptance_criteria"] = re.ReplaceAllString(conn.AcceptanceCriteria, replacement)
			}
			if len(updates) == 0 {
				continue
			}
			opts := IssueTableOpts{UseWispsTable: isWisp[connID]}
			if err := u.issueRepo.Update(ctx, connID, updates, actor, opts); err != nil {
				return len(touched), fmt.Errorf("rewrite refs %s: %w", connID, err)
			}
			touched[connID] = true
			if desc, ok := updates["description"].(string); ok {
				conn.Description = desc
			}
			if notes, ok := updates["notes"].(string); ok {
				conn.Notes = notes
			}
			if design, ok := updates["design"].(string); ok {
				conn.Design = design
			}
			if ac, ok := updates["acceptance_criteria"].(string); ok {
				conn.AcceptanceCriteria = ac
			}
		}
	}
	return len(touched), nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped driver error: fix locks/connectivity first (the failing issue ID is embedded in the message).
  2. Retry the delete once the database is idle; avoid concurrent bd writers on the same store.
  3. Check whether the target row still exists — a concurrently deleted neighbor makes the UPDATE fail.
  4. If context cancellation is the cause, run the delete with a dedicated long-timeout context.
  5. Manually resolve the reference in the named issue, then retry the delete.

Example fix

// before: two concurrent bd processes delete/update at once
//   error: rewrite refs bd-42: database is locked
// after: serialize mutations (single writer, or an external lock)
lock := acquireStoreLock()
defer lock.Release()
deleteErr := deleter.DeleteMany(ctx, ids)
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the target issues still exist before the delete triggers rewrites
for _, id := range connectedIDs {
    if _, err := usecase.GetIssue(ctx, id); err != nil {
        return fmt.Errorf("connected issue %s unavailable: %w", id, err)
    }
}

Type guard

func isRewriteRefsErr(err error) (string, bool) {
    if err == nil { return "", false }
    var id string
    if _, scanErr := fmt.Sscanf(err.Error(), "rewrite refs %s:", &id); scanErr == nil {
        return id, true
    }
    return "", false
}

Try / catch

err := usecase.DeleteMany(ctx, ids)
if err != nil && strings.Contains(err.Error(), "rewrite refs ") {
    // extract failing issue id from message, check locks/row state, retry
    cause := errors.Unwrap(err)
    return fmt.Errorf("delete aborted during ref rewrite: %w", cause)
}

Prevention

When it happens

Trigger: deleteMany where a surviving neighbor issue contains a textual reference to a deleted ID and the UPDATE on that neighbor (issues or wisps table depending on isWisp) fails — DB lock, connection error, context canceled, or constraint violation.

Common situations: Deleting issues concurrently with another writer holding row locks; database going away mid-batch; updating a wisp row in a store whose wisp table is in a bad state; long-running deletes hitting driver timeouts.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/09cd508b190e3b82. Report an issue: GitHub.