{"record":{"id":"cd06b2ae03ff26fc","repo":"gastownhall/beads","slug":"rename-lease-row-w","errorCode":null,"errorMessage":"rename lease row: %w","messagePattern":"rename lease row: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/bulk_ops.go","lineNumber":314,"sourceCode":"\t\tUPDATE issues\n\t\tSET id = ?, title = ?, description = ?, design = ?, acceptance_criteria = ?, notes = ?, updated_at = ?\n\t\tWHERE id = ?\n\t`, newID, issue.Title, issue.Description, issue.Design, issue.AcceptanceCriteria, issue.Notes, now, oldID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"update issue ID: %w\", err)\n\t}\n\tif rows, _ := result.RowsAffected(); rows == 0 {\n\t\treturn fmt.Errorf(\"issue not found: %s\", oldID)\n\t}\n\n\tif err := UpdateIssueIDInDependenciesInTx(ctx, tx, oldID, newID); err != nil {\n\t\treturn err\n\t}\n\n\t// A live lease follows its issue across the rename.\n\tif _, err := tx.ExecContext(ctx,\n\t\t`UPDATE leases SET issue_id = ? WHERE issue_id = ?`, newID, oldID); err != nil {\n\t\treturn fmt.Errorf(\"rename lease row: %w\", err)\n\t}\n\n\treturn InsertDerivedEvent(ctx, tx, \"events\", AuxEvent{\n\t\tIssueID:   newID,\n\t\tEventType: \"renamed\",\n\t\tActor:     actor,\n\t\tOldValue:  str(oldID),\n\t\tNewValue:  str(newID),\n\t})\n}\n\nfunc updateWispIDInTx(ctx context.Context, tx *sql.Tx, oldID, newID string, issue *types.Issue, actor string) error {\n\tnow := time.Now().UTC()\n\tresult, err := tx.ExecContext(ctx, `\n\t\tUPDATE wisps\n\t\tSET id = ?, title = ?, description = ?, design = ?, acceptance_criteria = ?, notes = ?, updated_at = ?\n\t\tWHERE id = ?\n\t`, newID, issue.Title, issue.Description, issue.Design, issue.AcceptanceCriteria, issue.Notes, now, oldID)","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/bulk_ops.go#L296-L332","documentation":"After the issue row itself was renamed, the library moves any live lease row to the new ID (`UPDATE leases SET issue_id = ?`). This error wraps a failure of that update. The whole rename transaction rolls back, so the issue keeps its old ID and its lease.","triggerScenarios":"Calling UpdateIssueIDInTx while the issue holds an active lease and the leases UPDATE fails — driver error, lock contention on the leases row, or schema mismatch on the leases table.","commonSituations":"Another worker holds/contends the lease row concurrently; connection drop mid-transaction; leases table schema changed between versions; storage backend under heavy load causing lock timeouts.","solutions":["Retry the rename after the conflicting worker releases the lease (check wrapped error for lock timeout).","Verify the leases table schema matches the current migration set.","Check storage backend health/latency; increase lock timeout if the driver supports it.","If a stale lease blocks the rename, release the lease through the supported API, then retry."],"exampleFix":"// before\nstore.UpdateIssueID(ctx, oldID, newID, issue, actor) // fails: lease row locked by worker\n// after\nstore.ReleaseLease(ctx, oldID) // or wait for lease expiry\nerr := store.UpdateIssueID(ctx, oldID, newID, issue, actor)","handlingStrategy":"retry","validationCode":"if lease, _ := store.GetLease(ctx, oldID); lease != nil {\n    // consider releasing or waiting for lease expiry before renaming\n}","typeGuard":null,"tryCatchPattern":"for attempt := 0; attempt < 3; attempt++ {\n    err := store.UpdateIssueID(ctx, oldID, newID, issue, actor)\n    if err == nil { break }\n    if strings.Contains(err.Error(), \"rename lease row:\") && isLockTimeout(err) {\n        time.Sleep(backoff(attempt)); continue\n    }\n    return err\n}","preventionTips":["Don't rename issues with live leases; release or wait for expiry.","Serialize renames against background workers that take leases.","Monitor storage lock-timeout metrics under load."],"tags":["database","lease","rename","lock-contention"],"backgroundTag":"row-lock-contention","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}