{"record":{"id":"552328126cb3a8f0","repo":"gastownhall/beads","slug":"failed-to-update-issue-w-552328","errorCode":null,"errorMessage":"failed to update issue: %w","messagePattern":"failed to update issue: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/update.go","lineNumber":490,"sourceCode":"\n\t// Auto-manage leases when direct updates change status or assignee.\n\t// Clears stale leases only; arming is reserved for claim/heartbeat.\n\tclearLease := ManageLeaseOnUpdate(oldIssue, updates)\n\n\t// Rewrite row_lock on every update so a concurrent status/ownership\n\t// mutation (reclaim/close) collides on this shared cell and is forced to\n\t// conflict-and-retry rather than silently cell-merging two writes to\n\t// different columns of the same row (see lease.go). This is the \"every\n\t// mutating path writes row_lock\" invariant the lease scheme depends on.\n\tsetClauses = append(setClauses, \"row_lock = ?\")\n\targs = append(args, freshRowLock())\n\n\targs = append(args, id)\n\n\t//nolint:gosec // G201: issueTable comes from WispTableRouting (hardcoded constants)\n\tquery := fmt.Sprintf(\"UPDATE %s SET %s WHERE id = ?\", issueTable, strings.Join(setClauses, \", \"))\n\tif _, err := tx.ExecContext(ctx, query, args...); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to update issue: %w\", err)\n\t}\n\n\tif clearLease {\n\t\tif err := DeleteLeaseInTx(ctx, tx, id); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif recordEvent {\n\t\toldData, _ := json.Marshal(oldIssue)\n\t\tnewData, _ := json.Marshal(updates)\n\t\teventType := DetermineEventType(oldIssue, updates)\n\n\t\tif err := RecordFullEventInTable(ctx, tx, eventTable, id, eventType, actor, string(oldData), string(newData)); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to record event: %w\", err)\n\t\t}\n\t}\n","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/update.go#L472-L508","documentation":"The UPDATE statement executed inside updateIssueInTx failed at the database driver level. The driver error is wrapped as \"failed to update issue: %w\", and the transaction will typically be rolled back by the caller.","triggerScenarios":"tx.ExecContext fails: constraint violations (e.g. NOT NULL, CHECK on status), value type mismatches with the column schema, table lock/timeout, connection loss, or an invalid value for a newly-allowed field not matching the schema.","commonSituations":"Setting a column to a type the driver can't bind; Dolt server restart or dropped connection mid-transaction; schema drift where a column is stricter than the code expects; unique/PK conflicts via dependent tables.","solutions":["Inspect the wrapped driver error for the SQL state/constraint message.","Verify all update values match column types and constraints (run the equivalent UPDATE manually).","Check database connectivity and retry the transaction if the error is transient (connection refused/timeout).","Confirm the schema matches the version of the code (run migrations/bd doctor)."],"exampleFix":"// before\nif _, err := tx.ExecContext(ctx, query, args...); err != nil {\n    return nil, fmt.Errorf(\"failed to update issue: %w\", err) // opaque\n}\n// after\nif _, err := tx.ExecContext(ctx, query, args...); err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        return nil, err // retryable: caller re-runs tx\n    }\n    return nil, fmt.Errorf(\"failed to update issue: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// pre-check values against schema constraints before the call\nif s, ok := updates[\"status\"]; ok {\n    if !types.IsValidStatus(fmt.Sprintf(\"%v\", s)) {\n        return fmt.Errorf(\"invalid status %v\", s)\n    }\n}","typeGuard":null,"tryCatchPattern":"if _, err := storage.UpdateIssue(ctx, id, updates, actor); err != nil {\n    if strings.Contains(err.Error(), \"failed to update issue\") {\n        var retriable = errors.Is(err, context.DeadlineExceeded) || isConnectionError(err)\n        if retriable { /* reopen tx and retry */ }\n    }\n    return err\n}","preventionTips":["Ensure update values match column types and constraints.","Keep transactions short to avoid locks/timeouts.","Handle transient DB errors (Dolt restarts, dropped conns) with bounded retries.","Run schema checks (bd doctor) after version upgrades to catch drift."],"tags":["storage","sql","transaction","database"],"backgroundTag":"sql-update-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}