OpenNHP/opennhp · warning
keystore: sweep otp rows affected
Error message
keystore: sweep otp rows affected: %w
What it means
SweepStaleOTPs wraps the failure of res.RowsAffected() after the OTP DELETE succeeded. Same driver-level limitation as the agent_keys sweep: some drivers/Result implementations cannot report the number of deleted rows.
Solutions
- Verify the SQLite driver implements RowsAffected; upgrade or switch drivers if it does not.
- If the deleted count is only for logging, log the error and continue instead of failing the sweep.
- Pin known-good driver versions in go.mod to avoid behavior changes on upgrade.
- If an exact count matters, wrap the DELETE in a transaction and pre-count matching rows.
Example fix
// before
n, err := res.RowsAffected()
if err != nil {
return 0, fmt.Errorf("keystore: sweep otp rows affected: %w", err)
}
// after
n, err := res.RowsAffected()
if err != nil {
log.Warnf("keystore: otp sweep completed, count unavailable: %v", err)
n = -1
} Defensive patterns
Strategy: fallback
Validate before calling
null
Type guard
n, err := res.RowsAffected()
if err != nil {
db.QueryRow("SELECT COUNT(*) FROM otp_records WHERE created_at < ? AND (used=1 OR expires_at <= ?)", cutoff, time.Now().Unix()).Scan(&remaining)
} Try / catch
n, err := res.RowsAffected()
if err != nil {
log.Warnf("otp sweep count unavailable: %v", err)
n = -1
} Prevention
- Prefer SQLite drivers with full Result metadata
- Keep driver versions pinned and smoke-test RowsAffected in CI
- Treat the count as best-effort telemetry, not a correctness requirement
- If counts matter, pre-compute matches in the same transaction
When it happens
Trigger: Calling SweepStaleOTPs against a driver whose Result lacks RowsAffected support, even though the DELETE executed.
Common situations: Driver swap or version upgrade in go.mod; custom driver wrappers that drop row-count metadata; embedded SQLite variants with limited metadata support.
Related errors
- keystore: sweep rows affected
- keystore: open database
- keystore: get agent key
- keystore: find agent by pubkey
- keystore: check agent registered
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/1132661b8f0d28f7.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/server/keystore.go:566
// to 86400s (24 hours) when passed a negative value. Pass 0 to delete all
// used or expired OTPs regardless of age.
func (s *AgentKeyStore) SweepStaleOTPs(retentionSeconds int64) (int64, error) {
if retentionSeconds < 0 {
retentionSeconds = 86400
}
cutoff := time.Now().Unix() - retentionSeconds
res, err := s.db.Exec(
`DELETE FROM otp_records
WHERE created_at < ?
AND (used = 1 OR expires_at <= ?)`,
cutoff, time.Now().Unix(),
)
if err != nil {
return 0, fmt.Errorf("keystore: sweep otp: %w", err)
}
n, err := res.RowsAffected()
if err != nil {
return 0, fmt.Errorf("keystore: sweep otp rows affected: %w", err)
}
return n, nil
}
// ── Helpers ───────────────────────────────────────────────────────────────
func randomDigits(n int) (string, error) {
if n <= 0 {
return "", fmt.Errorf("invalid digit count: %d", n)
}
buf := make([]byte, n)
for i := range buf {
digit, err := rand.Int(rand.Reader, big.NewInt(10))
if err != nil {
return "", err
}
buf[i] = byte('0') + byte(digit.Int64())View on GitHub (pinned to 6e04ca5ff0)