- Error code
- ORA-00060
- Product
- Oracle Database
- Versions
- Oracle Database 19c, 21c, 23ai and 26ai (same wording)
- Category
- Locking, Transactions & Sessions
- Last reviewed
- 24 October 2026
On this page
What it means
ORA-00060 means two or more transactions were each waiting for a lock the other held, so Oracle broke the cycle by rolling back one statement in one of them and returning this error to that session.
A deadlock is a cycle of waits: session A holds a row lock that session B needs, while B holds a row lock that A needs. Neither can ever proceed, so Oracle detects the cycle itself. Oracle's Concepts guide says it resolves a deadlock by rolling back one statement involved in it, and returns ORA-00060 to the transaction that detected the deadlock. Only that statement is undone: the rest of the transaction, and all the locks it already holds, are still in place.
That last point matters. Until the session that received ORA-00060 commits or rolls back, the other session is usually still waiting on it. Oracle's guidance is that the signalled transaction should normally be rolled back explicitly, although it can retry the rolled-back statement after waiting. Oracle also writes a trace file that shows the sessions, the SQL and the resources involved; the error reference's Action is to read that trace file.
Because Oracle locks rows rather than pages, never escalates locks and does not lock rows for queries, deadlocks are uncommon. When they happen, the usual causes are transactions that lock the same rows in a different order, and foreign keys without an index, which make Oracle take a table lock on the child table.
Common causes
- Two transactions update the same rows in opposite orders, for example one processes ids in ascending order and another in descending order.
- A foreign key column in a child table has no index, so deleting a parent row or changing its key locks the whole child table (Oracle's Concepts guide says deadlocks then occur more often).
- An application keeps a transaction open while waiting for user input or a remote call, so its row locks are held long enough to collide with other work.
- Explicit locking with LOCK TABLE or SELECT ... FOR UPDATE in a different order in different code paths, which Oracle names as the most common source of deadlocks.
- Parallel batch jobs that split the same table by ranges that overlap.
Diagnose it
Read-only These queries only read the data dictionary (USER_, ALL_ and DBA_ views) and dynamic performance views (V$). They do not change data, privileges or settings, so they are safe to run first. DBA_ and V$ views need the matching privileges.
Find the trace file Oracle wrote
The deadlock is recorded in the alert log with the name of a trace file. The trace file shows the deadlock graph: which sessions held and waited for which locks, and the SQL each was running. V$DIAG_INFO shows where the trace directory is (it needs SELECT access to V$ views, normally a DBA).
SELECT name, value
FROM v$diag_info
WHERE name IN ('Diag Trace', 'Default Trace File'); The trace directory on the database server. Look for the trace file named in the alert log at the time of the error; the alert log itself is in the same directory.
Look for foreign keys without an index
This lists foreign key columns in your schema that are not the leading column of any index. It checks single-column keys well; for multi-column keys, check that an index starts with all the key columns.
SELECT c.table_name, c.constraint_name, cc.column_name
FROM user_constraints c
JOIN user_cons_columns cc
ON cc.constraint_name = c.constraint_name
WHERE c.constraint_type = 'R'
AND NOT EXISTS (
SELECT 1
FROM user_ind_columns ic
WHERE ic.table_name = cc.table_name
AND ic.column_name = cc.column_name
AND ic.column_position = cc.position
)
ORDER BY c.table_name, c.constraint_name; Each foreign key column with no supporting index. If the deadlock graph names one of these tables, index the column.
See who is waiting on whom right now
Deadlocks are resolved within seconds, but if sessions keep blocking each other this shows the current chains. Needs SELECT access to V$SESSION.
SELECT sid, serial#, username, blocking_session, event, seconds_in_wait, sql_id, module
FROM v$session
WHERE blocking_session IS NOT NULL
ORDER BY seconds_in_wait DESC; Sessions that are waiting and the SID that blocks each one. Two sessions that block each other would be a deadlock; Oracle breaks such a cycle itself.
How to fix it
Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.
Roll back the transaction and retry it
Changes dataAfter ORA-00060 only the last statement was undone. Roll back the whole transaction so its locks are released and the other session can finish, then run the transaction again. Make the retry automatic in the application, with a limit on the number of attempts.
ROLLBACK;
-- then run the whole transaction again from the start
Lock rows in one consistent order
Changes dataIf every transaction that touches several rows locks them in the same order, for example by ascending primary key, a cycle of waits cannot form. A transfer between two accounts can lock both rows first, one statement per row with the lower id first (the application sorts the two ids), before changing either.
-- :low_id is the smaller of the two account ids, :high_id the larger
SELECT balance FROM shop.accounts WHERE id = :low_id FOR UPDATE;
SELECT balance FROM shop.accounts WHERE id = :high_id FOR UPDATE;
UPDATE shop.accounts SET balance = balance - :amount WHERE id = :from_id;
UPDATE shop.accounts SET balance = balance + :amount WHERE id = :to_id;
COMMIT;
Index foreign key columns
Changes schemaOracle's Concepts guide says foreign keys should almost always be indexed, unless the parent key is never updated or deleted. With the index, deleting a parent row no longer locks the whole child table.
Caution: Building an index on a large, busy table takes time and space; Oracle's CREATE INDEX reference describes the ONLINE option, which allows DML on the table while the index is built.
CREATE INDEX order_lines_order_id_ix ON shop.order_lines (order_id);
Keep transactions short
Client or network sideCommit as soon as a unit of work is complete, and never keep a transaction open while waiting for a user or a remote service. Fewer locks held for less time means fewer chances to collide.
Examples
Two sessions updating the same two rows in opposite order
Not reproduced here: no Oracle Database is installed for this site. The message text is from Oracle's error reference, and the sequence follows the deadlock example in Oracle's Concepts guide. Which of the two sessions receives the error is not fixed: Oracle says either can; the table and values are illustrative.
-- session 1
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- session 2
UPDATE accounts SET balance = balance - 50 WHERE id = 2;
-- session 1 (waits for session 2)
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- session 2 (waits for session 1: a cycle)
UPDATE accounts SET balance = balance + 50 WHERE id = 1;
ORA-00060: deadlock detected while waiting for resource
Two batch jobs that walk the same rows in different orders
Not reproduced here: no Oracle Database is installed for this site. The message format is from Oracle's error reference; the ids and table are illustrative, and whether the jobs deadlock depends on how their statements interleave. Making both jobs use the same order (or committing each row before the next) removes the cycle.
-- job A: pays invoices in ascending id order, one row at a time
UPDATE invoices SET status = 'PAID' WHERE id = 1001;
UPDATE invoices SET status = 'PAID' WHERE id = 1002;
-- job B, at the same time: archives in descending id order
UPDATE invoices SET archived = 'Y' WHERE id = 1002;
UPDATE invoices SET archived = 'Y' WHERE id = 1001;
ORA-00060: deadlock detected while waiting for resource
How to prevent it
Decide on one order for locking related rows (for example by primary key) and use it in every code path. Index foreign key columns unless the parent key is never updated or deleted. Keep transactions short and never wait for a user while holding locks.
Treat ORA-00060 as a retryable error in the application: roll back, wait briefly and retry a limited number of times, and log each occurrence so that a pattern can be traced to its pair of statements in the trace file.
Common questions
Does ORA-00060 roll back my whole transaction?
No. Oracle rolls back only the statement that detected the deadlock. Earlier changes in the transaction and their locks remain until you commit or roll back, so normally you roll back explicitly and retry.
Where do I see what caused the deadlock?
In the trace file Oracle writes when it detects the deadlock, named in the alert log. It shows the deadlock graph with the sessions, the locks they held and waited for, and their SQL. V$DIAG_INFO shows the trace directory.
Is ORA-00060 the same as ORA-00054?
No. ORA-00060 is a cycle of sessions waiting on each other, which Oracle breaks. ORA-00054 means one session would not wait (NOWAIT or a timeout) for a lock another session holds; no cycle is involved.
Learn more on CodeWithSQL
Official documentation
- ORA-00060 (Oracle Database Error Messages)
- Data Concurrency and Consistency: Locks and Deadlocks (Oracle Database Concepts 19c)
- V$DIAG_INFO (Oracle Database Reference 19c)
- CREATE INDEX (Oracle SQL Language Reference 19c)
Last technically reviewed 24 October 2026; examples and diagnostics run on Oracle Database Error Messages reference (26ai, 21c and 19c); not run locally. Messages and explanations follow Oracle's Database Error Messages reference; no Oracle Database was available, so the examples were not run locally.