gastownhall/beads · error

WakeExpiredDefers: %w

Error message

WakeExpiredDefers: %w

What it means

Wrapped error from WakeExpiredDefers: the repo call that flips expired DATED defers back to open failed and is prefixed with "WakeExpiredDefers: ". The method returns counts of woken permanent issues and wisps; on this error both counts are zeroed and the wake did not complete. The correct transaction form depends on whether permanent issues or only wisps woke.

Source

Thrown at internal/storage/domain/issue.go:1929

	if id == "" {
		return fmt.Errorf("Heartbeat: id must not be empty")
	}
	if err := u.issueRepo.HeartbeatIssue(ctx, id, actor); err != nil {
		return fmt.Errorf("Heartbeat: %w", err)
	}
	return nil
}

// WakeExpiredDefers returns every expired DATED defer to open (see
// issueops.WakeExpiredDefersInTx) and reports how many permanent issues and
// wisps woke. The caller owns persistence: commit with a wake message iff
// issues > 0, and with the ephemeral plain-COMMIT form iff only wisps woke
// (wisp tables are dolt_ignored, so their wake needs a SQL commit but must
// mint no version commit).
func (u *issueUseCaseImpl) WakeExpiredDefers(ctx context.Context) (issues, wisps int, err error) {
	issues, wisps, err = u.issueRepo.WakeExpiredDefers(ctx)
	if err != nil {
		return 0, 0, fmt.Errorf("WakeExpiredDefers: %w", err)
	}
	return issues, wisps, nil
}

func (u *issueUseCaseImpl) ReclaimExpiredLeases(ctx context.Context, olderThan time.Duration, filter types.ReclaimFilter, actor string) ([]types.ReclaimedLease, error) {
	out, err := u.issueRepo.ReclaimExpiredLeases(ctx, olderThan, filter, actor)
	if err != nil {
		return nil, fmt.Errorf("ReclaimExpiredLeases: %w", err)
	}
	return out, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap the error to find the underlying repository/transaction cause
  2. Check database connectivity and retry the wake sweep
  3. Ensure the sweep runs with the correct tx form: version-commit when issues > 0, plain ephemeral commit when only wisps woke
  4. Look for lock contention from concurrent bd processes and serialize the sweep

Example fix

// before
i, w, err := uc.WakeExpiredDefers(ctx)
if err != nil { return err }
// after
i, w, err := uc.WakeExpiredDefers(ctx)
if err != nil {
	log.Printf("defer wake failed, will retry: %v", err)
	return retryWithBackoff(func() error { _, _, err = uc.WakeExpiredDefers(ctx); return err })
}
Defensive patterns

Strategy: retry

Try / catch

issues, wisps, err := uc.WakeExpiredDefers(ctx)
if err != nil {
	return retryWithBackoff(func() error {
		issues, wisps, err = uc.WakeExpiredDefers(ctx)
		return err
	})
}

Prevention

When it happens

Trigger: Calling WakeExpiredDefers(ctx) when issueRepo.WakeExpiredDefers fails: database unavailable, transaction/commit error (e.g. wrong tx form when permanent issues are involved and a version commit is required), or SQL error in the wake scan.

Common situations: Periodic defer-wakeup jobs (cron/daemon) hitting a DB outage; concurrent writers conflicting with the wake transaction; misconfigured ephemeral-vs-versioned commit mode.

Related errors


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