phacility/phabricator · warning · AphrontLockTimeoutQueryException

1205

1205

Error message

#%d: %s

What it means

MySQL errno 1205 'Lock wait timeout exceeded', translated into AphrontLockTimeoutQueryException. Unlike a deadlock, this fires when a transaction waited longer than innodb_lock_wait_timeout (default 50s) for a lock held by ANOTHER still-running transaction — typically a long-running transaction, a stuck connection holding row locks, or a metadata lock from a pending DDL. It is retriable in the sense that retrying once the lock holder finishes usually succeeds.

Source

Thrown at src/infrastructure/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php:340

  }

  private function throwCommonException($errno, $error) {
    $message = pht('#%d: %s', $errno, $error);

    switch ($errno) {
      case 2013: // Connection Dropped
        throw new AphrontConnectionLostQueryException($message);
      case 2006: // Gone Away
        $more = pht(
          'This error may occur if your configured MySQL "wait_timeout" or '.
          '"max_allowed_packet" values are too small. This may also indicate '.
          'that something used the MySQL "KILL <process>" command to kill '.
          'the connection running the query.');
        throw new AphrontConnectionLostQueryException("{$message}\n\n{$more}");
      case 1213: // Deadlock
        throw new AphrontDeadlockQueryException($message);
      case 1205: // Lock wait timeout exceeded
        throw new AphrontLockTimeoutQueryException($message);
      case 1062: // Duplicate Key
        // NOTE: In some versions of MySQL we get a key name back here, but
        // older versions just give us a key index ("key 2") so it's not
        // portable to parse the key out of the error and attach it to the
        // exception.
        throw new AphrontDuplicateKeyQueryException($message);
      case 1044: // Access denied to database
      case 1142: // Access denied to table
      case 1143: // Access denied to column
      case 1227: // Access denied (e.g., no SUPER for SHOW SLAVE STATUS).

        // See T13622. Try to help users figure out that this is a GRANT
        // problem.

        $more = pht(
          'This error usually indicates that you need to "GRANT" the '.
          'MySQL user additional permissions. See "GRANT" in the MySQL '.
          'manual for help.');

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Find and finish/kill the blocking transaction: SHOW ENGINE INNODB STATUS, SELECT ... FROM information_schema.innodb_trx ordered by trx_started
  2. Retry the failed write once the blocker is gone
  3. Shrink the offending transaction — batch large updates into smaller commits so locks are held briefly
  4. Schedule DDL migrations during low traffic to avoid metadata-lock pileups
  5. Optionally raise innodb_lock_wait_timeout if legitimate long lock waits are expected

Example fix

-- diagnose: find long-running transactions holding locks
SELECT trx_id, trx_started, trx_mysql_thread_id
  FROM information_schema.innodb_trx
  ORDER BY trx_started LIMIT 5;

-- then either commit/kill the blocker, or retry the timed-out write once it clears
Defensive patterns

Strategy: retry

Try / catch

try {
  $object->openTransaction(12);
  /* ...writes... */
  $object->saveTransaction();
} catch (AphrontLockTimeoutQueryException $ex) {
  $object->killTransaction();
  // first identify/finish the blocking transaction, then retry once
}

Prevention

When it happens

Trigger: A long transaction (bulk edit, migration, paused debugger session) holds row locks while other writers touch the same rows; a schema change (ALTER TABLE) waiting on a metadata lock blocks writers behind it; a forgotten 'START TRANSACTION' in a manual session pinning locks.

Common situations: Long-running admin scripts updating thousands of objects; bin/storage upgrade/adjust running during traffic; a developer's open MySQL client with an uncommitted transaction blocking production writers.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/75e1392c8e20ab67. Report an issue: GitHub.