NationalSecurityAgency/ghidra · error · DomainObjectLockedException

Could not get lock

Error message

Could not get lock

What it means

DomainObjectLockHold.lock wraps DomainObject.lock(reason). When lock returns false the domain object is already held by another transaction/thread, so DomainObjectLockedException is thrown. This is an expected concurrency-conflict failure (not a bug): only one writer may hold the modification lock at a time.

Source

Thrown at Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DomainObjectLockHold.java:49

 * 	// Do stuff while holding the lock
 * }
 * </pre>
 */
public interface DomainObjectLockHold extends AutoCloseable {

	/**
	 * Wrapper for {@link DomainObject#lock(String)}
	 * 
	 * @param object the object
	 * @param reason as in {@link DomainObject#lock(String)}
	 * @return the hold, which should be used in a {@code try-with-resources} block
	 * @throws DomainObjectLockedException if the lock could not be obtained
	 */
	static DomainObjectLockHold lock(DomainObject object, String reason) {
		if (object.lock(reason)) {
			return new DefaultHold(object);
		}
		throw new DomainObjectLockedException("Could not get lock");
	}

	/**
	 * Wrapper for {@link DomainObject#forceLock(boolean, String)}
	 * 
	 * @param object the object
	 * @param rollback as in {@link DomainObject#forceLock(boolean, String)}
	 * @param reason as in {@link DomainObject#forceLock(boolean, String)}
	 * @return the hold, which should be used in a {@code try-with-resources} block
	 */
	static DomainObjectLockHold forceLock(DomainObject object, boolean rollback, String reason) {
		object.forceLock(rollback, reason);
		return new DefaultHold(object);
	}

	class DefaultHold implements DomainObjectLockHold {
		final DomainObject object;

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Use try-with-resources around DomainObjectLockHold.lock and handle DomainObjectLockedException (retry with backoff or abort gracefully).
  2. Shorten the critical section - acquire the lock as late as possible and close it promptly.
  3. Make sure every prior hold is closed (no leaked AutoCloseable) so the object isn't permanently locked.
  4. As a last resort for administrative recovery, DomainObjectLockHold.forceLock overrides the lock - but this disrupts any other active transaction.

Example fix

// before
try (var hold = DomainObjectLockHold.lock(obj, "edit")) {
    mutate(obj);
}
// after
int attempts = 0;
while (true) {
    try (var hold = DomainObjectLockHold.lock(obj, "edit")) {
        mutate(obj);
        break;
    } catch (DomainObjectLockedException e) {
        if (++attempts > 5) throw e;
        Thread.sleep(50);
    }
}
Defensive patterns

Strategy: retry

Try / catch

int attempt = 0;
while (true) {
    try (var hold = DomainObjectLockHold.lock(obj, "edit")) {
        mutate(obj);
        break;
    } catch (DomainObjectLockedException e) {
        if (++attempt > MAX_ATTEMPTS) throw e;
        Thread.sleep(BACKOFF_MS);
    }
}

Prevention

When it happens

Trigger: object.lock(reason) returns false - another holder currently owns the modification lock (another thread editing the program, a long-running transaction, the GUI mid-edit, or a re-entrant attempt that the implementation disallows).

Common situations: Concurrent edits to one DomainObject; a background task locking while the GUI holds the lock; tight test loops that race on the same object; a leaked/unclosed lock hold keeping the object locked.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/170c708eab16b793. Report an issue: GitHub.