Skip to content
Oracle Database

Oracle Error ORA-00060

deadlock detected while waiting for resource

Applies to: Oracle Database 19c, 21c, 23ai and 26ai (same wording)
Last technically reviewed 24 October 2026 on Oracle Database Error Messages reference (26ai, 21c and 19c); not run locally
The full message
ORA-00060: deadlock detected while waiting for resource
Other forms of this message
SQL*Plus and SQLcl show the failing line first
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Raised inside PL/SQL (the procedure name and line number are illustrative)
ORA-00060: deadlock detected while waiting for resource
ORA-06512: at "SHOP.PAY_INVOICES", line 9
Error code
ORA-00060
Product
Oracle Database
Versions
Oracle Database 19c, 21c, 23ai and 26ai (same wording)
Last reviewed
24 October 2026

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');
What to look for

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;
What to look for

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;
What to look for

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 data

After 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 data

If 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 schema

Oracle'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 side

Commit 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;
Expected result (documented format, not run)
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;
Expected result (documented format, not run)
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

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.

Still stuck on an Oracle error?

Browse the full Oracle error library, or get hands-on help from an experienced SQL professional.