gastownhall/beads · error
release checked close savepoint: %w
Error message
release checked close savepoint: %w
What it means
Beads throws this when 'RELEASE SAVEPOINT <name>' fails after a checked close. Releasing discards the savepoint once the trial has succeeded (or been rolled back); a failure here means the transaction or connection is in trouble, even though the close itself may have been applied. It is often reported together with a rollback error via errors.Join.
Source
Thrown at internal/storage/issueops/close.go:259
func rollbackAndReleaseCloseCheckedSavepoint(ctx context.Context, tx DBTX, name string) error {
rollbackErr := rollbackToCloseCheckedSavepoint(ctx, tx, name)
releaseErr := releaseCloseCheckedSavepoint(ctx, tx, name)
return errors.Join(rollbackErr, releaseErr)
}
func rollbackToCloseCheckedSavepoint(ctx context.Context, tx DBTX, name string) error {
//nolint:gosec // G201: name is generated by createCloseCheckedSavepoint.
if _, err := tx.ExecContext(ctx, "ROLLBACK TO SAVEPOINT "+name); err != nil {
return fmt.Errorf("rollback checked close savepoint: %w", err)
}
return nil
}
func releaseCloseCheckedSavepoint(ctx context.Context, tx DBTX, name string) error {
//nolint:gosec // G201: name is generated by createCloseCheckedSavepoint.
if _, err := tx.ExecContext(ctx, "RELEASE SAVEPOINT "+name); err != nil {
return fmt.Errorf("release checked close savepoint: %w", err)
}
return nil
}
// dependencyTargetColumnForIDInTx returns the typed target column for id,
// preferring a durable row when an id is present in both tables.
func dependencyTargetColumnForIDInTx(ctx context.Context, tx DBTX, id string) (string, error) {
var found int
err := tx.QueryRowContext(ctx, "SELECT 1 FROM issues WHERE id = ?", id).Scan(&found)
if err == nil {
return "depends_on_issue_id", nil
}
if !errors.Is(err, sql.ErrNoRows) {
return "", fmt.Errorf("classify child-count target in issues: %w", err)
}
err = tx.QueryRowContext(ctx, "SELECT 1 FROM wisps WHERE id = ?", id).Scan(&found)
if err == nil {
return "depends_on_wisp_id", nilView on GitHub (pinned to 71377f2769)
Solutions
- Treat as a transaction-level failure: retry the entire close operation on a fresh transaction
- Look for an earlier joined error (errors.Join output) — the first error usually identifies the root cause
- Check backend/driver support for RELEASE SAVEPOINT semantics; upgrade driver if incomplete
- Investigate recurring deadlocks/lock timeouts if this happens under concurrent closes
Defensive patterns
Strategy: retry
Validate before calling
// Check transaction liveness before close-policy operations
if err := tx.QueryRowContext(ctx, "SELECT 1").Scan(&one); err != nil {
return fmt.Errorf("transaction not viable for checked close: %w", err)
} Try / catch
err := CloseIssueCheckedInTx(ctx, tx, id, opts)
if err != nil {
if dberrors.IsConnectionError(err) || dberrors.IsDeadlock(err) {
// savepoint may have been implicitly destroyed — restart on a fresh tx
return retryCloseOnNewTx(ctx, id)
}
return err
} Prevention
- Retry the whole close on fresh transactions after savepoint release failures
- Read errors.Join output: the first joined error usually names the real cause
- Avoid extremely long transactions that raise the odds of implicit rollback
- Keep driver/server versions aligned so savepoint semantics are consistent
When it happens
Trigger: CloseIssueCheckedInTx or rollbackAndReleaseCloseCheckedSavepoint calling RELEASE SAVEPOINT when the transaction was already rolled back (deadlock/timeout), the savepoint was already released, or the connection failed after the close UPDATE succeeded.
Common situations: Engine implicitly rolled back the transaction after a prior statement error; connection drop between the close UPDATE and RELEASE; driver that auto-releases savepoints on statement error.
Related errors
- create checked close savepoint: %w
- rollback checked close savepoint: %w
- failed to begin transaction: %w
- failed to commit is_blocked repairs: %w
- failed to set dolt_force_transaction_commit: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/dec573e27e891270.
Report an issue: GitHub.