{"record":{"id":"6b62d4e895bbe210","repo":"gastownhall/beads","slug":"db-issuesqlrepository-reclaimexpiredleases-w","errorCode":null,"errorMessage":"db: IssueSQLRepository.ReclaimExpiredLeases: %w","messagePattern":"db: IssueSQLRepository\\.ReclaimExpiredLeases: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue.go","lineNumber":1272,"sourceCode":"// repository's runner (the same DBTX-shaped seam ReclaimExpiredLeases uses)\n// and reports how many rows woke per table. The issues count decides whether\n// the transaction's owner mints a dolt commit; the wisps count decides\n// whether it must still issue a plain SQL commit — wisp tables are\n// dolt_ignored, so a wisp-only wake mints no version commit, but a caller\n// that treats it as \"nothing happened\" rolls the wisp writes back.\nfunc (r *issueSQLRepositoryImpl) WakeExpiredDefers(ctx context.Context) (issues, wisps int, err error) {\n\tout, err := issueops.WakeExpiredDefersInTx(ctx, r.runner)\n\tif err != nil {\n\t\treturn 0, 0, fmt.Errorf(\"db: IssueSQLRepository.WakeExpiredDefers: %w\", err)\n\t}\n\treturn len(out.Issues), len(out.Wisps), nil\n}\n\nfunc (r *issueSQLRepositoryImpl) ReclaimExpiredLeases(ctx context.Context, olderThan time.Duration, filter types.ReclaimFilter, actor string) ([]types.ReclaimedLease, error) {\n\tcutoff := time.Now().UTC().Add(-olderThan)\n\tout, err := issueops.ReclaimExpiredLeasesInTx(ctx, r.runner, cutoff, filter, actor)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"db: IssueSQLRepository.ReclaimExpiredLeases: %w\", err)\n\t}\n\treturn out, nil\n}\n\nconst deleteBatchSize = 200\n","sourceCodeStart":1254,"sourceCodeEnd":1278,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue.go#L1254-L1278","documentation":"Wraps a failure from issueops.ReclaimExpiredLeasesInTx, which finds leases older than the cutoff (optionally filtered) and reclaims them for the given actor, returning the reclaimed lease list. The wrapper only adds context; SQL, scan, or validation failures inside the helper surface here.","triggerScenarios":"Calling ReclaimExpiredLeases(ctx, olderThan, filter, actor) when the cutoff SELECT, the reclaim UPDATEs, or event writes fail: driver errors, invalid filter values producing bad SQL, missing tables, or actor/lease validation errors in the helper.","commonSituations":"Background reclaimer running against a dropped connection, invalid ReclaimFilter fields after an API change, clock skew producing odd cutoffs, or concurrent reclaimers contending on the same rows.","solutions":["Inspect the wrapped cause; validate the ReclaimFilter fields against the current types API.","Run a single reclaimer instance (or use locking) to avoid contention.","Retry transient driver errors; reclamation is idempotent for already-reclaimed leases.","Confirm lease/issues tables exist with current schema (migrations/doctor)."],"exampleFix":"// before\nout, err := repo.ReclaimExpiredLeases(ctx, olderThan, filter, actor)\nif err != nil { panic(err) }\n// after\nout, err := repo.ReclaimExpiredLeases(ctx, olderThan, filter, actor)\nif err != nil {\n    log.Printf(\"lease reclaim failed, retrying later: %v\", err)\n    return\n}","handlingStrategy":"retry","validationCode":"// sanity-check inputs before the call\nif olderThan <= 0 { return errors.New(\"olderThan must be positive\") }\nif actor == \"\" { return errors.New(\"actor required\") }\nif err := validateReclaimFilter(filter); err != nil { return err }","typeGuard":null,"tryCatchPattern":"out, err := repo.ReclaimExpiredLeases(ctx, olderThan, filter, actor)\nif err != nil {\n    if isTransientDBErr(err) { return retryReclaim(err) }\n    return fmt.Errorf(\"reclaim failed: %w\", err)\n}","preventionTips":["Run a single reclaimer instance (or leader-election) to avoid row contention.","Validate ReclaimFilter against current types constants after upgrades.","Use consistent UTC time handling for olderThan cutoffs.","Retry idempotently — already-reclaimed leases are harmless."],"tags":["database","lease","reclaim"],"backgroundTag":"lease-reclaim-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}