suideveloper
All questions
Transactions

Why does my transaction fail with an object version or locked object error?

November 12, 2026

Every Sui object has a version that increases each time a transaction changes it. Transactions name address-owned objects (including gas coins) by ID and exact version, and validators let only one transaction use any given version. You get a version error when your transaction points at a version that is already spent. You get a lock error when two transactions compete for the same version at the same time. The fix is almost always to rebuild with fresh object references and stop sending conflicting transactions in parallel.

How versioning causes this

A transaction sets the version of every object it touches to 1 + max(input versions). If you transfer object O at version 5 and pay gas with coin G at version 3, both come out at version 6. The gas coin changes in every transaction, even a failed one, because gas is charged from it.

For owned objects on the fast path, the sender states the version. When a validator signs a transaction it reserves (locks) those object versions for that transaction. Shared objects work differently: you name them by ID and their initial shared version, and consensus assigns the actual version. Shared objects don't produce these errors, but they can be delayed or cancelled under congestion.

The errors and what they mean

Stale version

Text
Transaction needs to be rebuilt because object 0x... version 12 (...) is
unavailable for consumption, current version: 13

Another transaction already used version 12. A common cause is reusing transaction bytes, or building a second transaction before the full node indexed the first one. Rebuild the transaction so it picks up the current version.

Reserved for another transaction

Text
Failed to sign transaction by a quorum of validators because one or more of
its objects is reserved for another transaction.

A transaction that is still in flight holds the same object version. Wait for it to finish, then rebuild and resubmit.

Equivocated until the next epoch

Text
Failed to sign transaction by a quorum of validators because one or more of
its objects is equivocated until the next epoch.

Two different transactions used the same owned object version, and validators split their votes so neither reached a quorum. That object version is locked until the epoch ends (about 24 hours on mainnet and testnet). This often happens to the gas coin when a script fires transactions in parallel from one address. Wait for the next epoch, then rebuild. Meanwhile, you can keep working with a different gas coin.

How to avoid it

  • Build right before you sign. In the TypeScript SDK, tx.object(id) resolves the latest version when the transaction is built. Don't cache built bytes.
  • Wait between dependent transactions. Reads come from indexed state, which trails execution slightly. Call waitForTransaction before building the next transaction that touches the same objects:
TypeScript
const result = await client.signAndExecuteTransaction({ transaction: tx1, signer })
await client.waitForTransaction({ result })
// now build tx2
  • Use the SDK executors. SerialTransactionExecutor queues transactions from one signer and caches object versions and the gas coin between them. ParallelTransactionExecutor (experimental) manages a pool of gas coins for parallel work:
TypeScript
import { SerialTransactionExecutor } from '@mysten/sui/transactions'

const executor = new SerialTransactionExecutor({ client, signer })
await Promise.all([executor.executeTransaction(tx1), executor.executeTransaction(tx2)])
  • Combine related steps into one programmable transaction block instead of several transactions that touch the same objects.
  • Give parallel jobs their own gas coins. Split a coin into several and pass each job its own coin with --gas or tx.setGasPayment().
  • Use a shared object when many users really do need to write to the same state. See shared vs owned objects.

Sources

More on transactions