Skip to content

ALC-3002: Row changed since you loaded it

A row you edited changed on the server after you loaded it. Alcuin committed none of your changes, so your edit cannot silently overwrite someone else’s.

The bar under the grid turns to a warning: A row changed under you. (or the number of rows), followed by Review to keep your edit on top of the current values or take theirs; the other changes stay staged. It offers Discard stale and reload and Review the stale row. Behind it, the engine’s message is Someone changed this row after you loaded it. Reload to see the current values.

The review after a row changed under you: the stale change is marked, and a block shows the row as loaded, now on the server, and yours, with Changed on the server: note. Keep mine and Take theirs are under it.

The review of a row that changed on the server since you loaded it.

In these cases nothing is marked stale, and the bar shows the engine’s message itself:

  • This row has not changed since you loaded it, but the server skipped the update: a row-level security policy or a BEFORE trigger on the table leaves it alone. The same message says delete for a deleted row.
  • This update would have changed 2 rows, not one, so nothing was changed. The table's key doesn't single out this row: a table that inherits from it may hold the same key. A delete says This delete would have removed 2 rows, not one, ….
  • The server added no row: a BEFORE trigger or a rule on the table skipped the insert., when a trigger or a rule skipped an insert.
  • The insert did not add exactly one row., when an insert added more than one row.

In the review, the stale change is marked changed under you, and Alcuin reads the row as it is now on the server.

Alcuin matches each edited row on its key and on the row version it loaded, xmin. The key is the table’s primary key or, when it has none, its smallest valid unique index whose columns are all NOT NULL, or which is NULLS NOT DISTINCT. Any update to the row since then, even one that writes the same values, gives it a new version. Every statement in a commit must touch exactly one row; if one does not, Alcuin rolls the whole commit back.

Common causes:

  • Another person, application or job updated or deleted the row.
  • You changed the row yourself, in a query tab.
  • A trigger on the table changed it.

Less common, and named in the message: the row has not changed, but the server skipped the statement.

  • A row-level security policy lets your role read the row but not update or delete it, so the statement matches nothing.
  • A BEFORE trigger returned NULL, which skips the statement for that row.
  1. Click Review the stale row.
  2. Read the server’s row. The review lists each column as loaded, now on the server and yours, and names the columns that changed, such as Changed on the server: status, updated_at. If you edited one of them too, it says Your edit to status would overwrite theirs.
  3. Choose for each stale change:
    • Keep mine writes only the columns you edited over the current row. For a deleted row, it deletes the row as it is now.
    • Take theirs drops your edit and leaves the server’s row.
    • If the review says Row no longer exists on the server., click Discard this change.
  4. Commit.

To drop every stale change at once and read the rows again, click Discard stale and reload. Your other staged changes stay staged.

If the row keeps changing under you because of a trigger or a busy job, make the change in a query tab with an UPDATE instead.

If the server skipped the statement, reviewing and committing again gives the same result. Look at the policies and triggers in the table’s structure, or ask whoever owns the table which rows your role may change. Click Discard changes to drop the table’s staged row changes, after Alcuin asks.

The engine reports this as ENGINE_STALE_ROW. Quote either name in an issue.