0X80320012

Fix Firebird FWP_E_TIMEOUT 0X80320012 Lock Timeout

Firebird transaction lock timeout on busy tables. Usually a missing index or long transaction holding a lock. Kill the blocker or tune the timeout.

Quick answer

Run SELECT * FROM MON$STATEMENTS and SELECT * FROM MON$TRANSACTIONS to find the transaction holding the lock, then kill it via gfix -k or commit/rollback from the app. If that's not possible, bump LockTimeout in firebird.conf to 15–20 seconds.

Why this happens

Firebird uses a lock manager to keep multiple transactions from stepping on each other. When transaction A updates a row or table, it holds a lock until commit or rollback. Transaction B comes along, tries to update the same row, and waits. If B doesn't get the lock within the time set by LockTimeout (default is 10 seconds), it throws FWP_E_TIMEOUT with the error code 0X80320012.

Most of the time, this isn't a random glitch. It's a pattern — someone left a transaction open in a dev tool, a report job runs long and holds a lock, or an app has a bug where it starts a transaction and never commits. Missing indexes also cause it because Firebird has to lock more rows during updates and deletes, making contention worse.

I've seen this exact error on Firebird 2.5 and 3.0 in production during end-of-day batch jobs. The culprit is almost always a forgotten COMMIT in a loop or a cursor that's still open.

Fix steps

1. Find the blocking transaction

If you're on Firebird 2.1 or newer, you have the MON$ tables. Open your SQL tool (ISQL, FlameRobin, or whatever you use) and run these two queries:

SELECT MON$STATEMENT_ID, MON$SQL_TEXT, MON$TRANSACTION_ID
FROM MON$STATEMENTS
WHERE MON$STATE = 1; -- active statements

SELECT MON$TRANSACTION_ID, MON$ATTACHMENT_ID, MON$STATE, MON$TIMESTAMP
FROM MON$TRANSACTIONS
WHERE MON$STATE = 1; -- active transactions

Look for a statement that's been running for a long time or a transaction that's been idle but still open. Those are your usual suspects. The MON$ATTACHMENT_ID is what you need to kill the connection.

2. Kill the blocking connection

Once you've got the attachment ID, you can disconnect it. If you're on Windows or Unix, run this from the command line:

gfix -k -attach <attachment_id> -user sysdba -password <password> <database_path>

This forces the attachment to roll back its active transaction and releases the locks. It's blunt, but it works. You'll see the error go away immediately.

If you don't have gfix handy, you can also kill the process from the OS side (Task Manager or kill on Unix). But that's riskier — it might leave the database in a weird state, though Firebird recovers on next connect.

3. If you can't kill the blocker, raise LockTimeout

Sometimes you can't kill the other transaction because it's a legit long-running process. For those cases, edit firebird.conf and find this line:

#LockTimeout = 10

Change it to something reasonable, like 20 or 30 seconds. The default 10 seconds is too aggressive for heavy batch operations. After you change it, restart Firebird.

LockTimeout = 20

Don't go crazy with 60+ seconds — users will hang and you'll get complaints about 'frozen' screens. 20–30 is the sweet spot.

If that doesn't fix it

If you're still getting timeouts after killing the blocker and bumping the timeout, the problem might be a missing index. When Firebird updates a table, it has to lock the rows that match the WHERE clause. Without an index, it does a full table scan and locks more rows than necessary, increasing the chance of collision.

Check your slow queries and the execution plans. If you see NATURAL scans in the plan for updates or deletes, create an index on the columns used in the WHERE clause. Example:

CREATE INDEX IDX_ORDERS_STATUS ON ORDERS (STATUS, ORDER_DATE);

Another possibility is that your application is using pessimistic locking unnecessarily. If you're doing SELECT ... FOR UPDATE and then updating, consider switching to optimistic concurrency (just update and check ROW_COUNT). That reduces lock holding time.

Also check your transaction isolation level. If you're using SNAPSHOT (read committed) in Firebird 3, that's fine. But if you're on CONSISTENCY, that takes table-level locks and causes more contention. Switch to read committed if your app allows it.

Prevention tips

The real fix here is to stop the pattern that causes it. Set a strict rule in your app: every transaction must be wrapped in try-catch-finally, and COMMIT or ROLLBACK must always happen.

Also run a periodic sweep for idle transactions. In Firebird, you can set IdleTimeout in firebird.conf to kill connections that sit idle too long:

IdleTimeout = 120

That's in seconds, so 120 means 2 minutes. If you've got apps that hold connections open and forget to commit, this will save you.

Last but not least, monitor your MON$ tables during peak hours. You'll see the problem coming before users experience it.

Related Errors in Database Errors
Seconds Behind Master Keeps Climbing? Check Slave Threads First 0X80092014 CRYPT_E_NOT_IN_REVOCATION_DATABASE: Fix 0x80092014 Now 0X00001AB8 Fix 0X00001AB8: Transaction Freeze Already in Progress 42P01 Postgres 42P01: Relation Does Not Exist After CREATE TABLE

Was this solution helpful?

EP
Erropedia Team
Tech Support Editors
The Erropedia editorial team researches and documents real-world tech errors from across Windows, Linux, macOS, networking, databases, cloud platforms, and more. Every solution is reviewed for accuracy and updated as software and systems evolve.