iflytek/astron-agent · critical
lock tenant bootstrap app failed
Error message
lock tenant bootstrap app failed: %w
What it means
After ensuring the bootstrap app row, the code takes a row lock with SELECT ... FOR UPDATE on tb_app for the reserved tenant ID and scans it back; any query/scan failure is wrapped as 'lock tenant bootstrap app failed'. The lock serializes concurrent replicas before credential rotation.
Solutions
- Inspect the wrapped cause: on lock-wait timeout, check innodb_lock_wait_timeout and which transaction holds the row (SHOW ENGINE INNODB STATUS).
- Ensure no other job deletes or modifies the reserved tenant row concurrently; protect it from cleanup scripts.
- Verify the bootstrap user has SELECT and locking capability on tb_app.
- Retry bootstrap after transient contention; the transactional design makes re-runs safe.
Example fix
// before: row deleted concurrently, Scan returns sql.ErrNoRows
err := transaction.QueryRowContext(ctx, `SELECT app_id, is_disable, is_delete FROM tb_app WHERE app_id = ? FOR UPDATE`, id).Scan(...)
// after: distinguish missing row from real failures
row := transaction.QueryRowContext(ctx, lockSQL, id)
if err := row.Scan(&lockedAppID, &lockedAppDisabled, &lockedAppDeleted); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return fmt.Errorf("reserved tenant app %s disappeared during bootstrap", id)
}
return fmt.Errorf("lock tenant bootstrap app failed: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
var one int
if err := db.QueryRow(`SELECT 1 FROM tb_app WHERE app_id = ?`, reservedID).Scan(&one); err != nil {
return fmt.Errorf("reserved app missing before bootstrap: %w", err)
} Try / catch
row := tx.QueryRowContext(ctx, lockSQL, id)
err := row.Scan(&a, &b, &c)
switch {
case errors.Is(err, sql.ErrNoRows):
return fmt.Errorf("reserved app vanished; rerun bootstrap")
case isLockWaitTimeout(err):
return retryErr // backoff and retry the whole transaction
case err != nil:
return fmt.Errorf("lock tenant bootstrap app failed: %w", err)
} Prevention
- Prevent concurrent replicas from deleting the reserved tenant row.
- Tune innodb_lock_wait_timeout for bootstrap workloads.
- Keep transactions brief and serialize bootstrap via an external lease if many replicas start at once.
When it happens
Trigger: QueryRowContext(...).Scan fails because the row vanished between insert and lock (concurrent delete), a connection error occurred, the MySQL user lacks SELECT/LOCK privilege, or Scan types mismatch the columns.
Common situations: Another process deleted the reserved row mid-transaction; connection dropped during long lock waits (innodb_lock_wait_timeout exceeded surfaces as a query error); replicas racing at startup contend on the same row and hit lock timeouts.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- begin tenant bootstrap transaction failed
- commit tenant bootstrap transaction failed
- adopt tenant bootstrap credential failed
- check tenant bootstrap API key ownership failed
- check tenant bootstrap managed credential failed
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/fa3c7d0d7d601a32.
Report an issue: GitHub.
Appendix: source
Thrown at core/tenant/tools/database/bootstrap_credentials.go:147
false,
"星辰租户",
false,
"",
); err != nil {
return fmt.Errorf("ensure tenant bootstrap app failed: %w", err)
}
// Serialize reconciliation across replicas on the reserved app row before
// taking any auth-index gap locks or rotating managed credentials.
var lockedAppID string
var lockedAppDisabled sql.NullBool
var lockedAppDeleted sql.NullBool
if err := transaction.QueryRowContext(
ctx,
`SELECT app_id, is_disable, is_delete FROM tb_app WHERE app_id = ? FOR UPDATE`,
credentials.TenantID,
).Scan(&lockedAppID, &lockedAppDisabled, &lockedAppDeleted); err != nil {
return fmt.Errorf("lock tenant bootstrap app failed: %w", err)
}
if lockedAppID != credentials.TenantID {
return errors.New("locked tenant bootstrap app does not match the reserved tenant ID")
}
if !lockedAppDisabled.Valid || lockedAppDisabled.Bool ||
!lockedAppDeleted.Valid || lockedAppDeleted.Bool {
return errors.New("reserved tenant bootstrap app is disabled or deleted")
}
return nil
}
func findTenantBootstrapCredential(
ctx context.Context,
transaction bootstrapTransaction,
credentials config.TenantBootstrapCredentials,
) (bool, error) {
var collisionOwner string
err := transaction.QueryRowContext(View on GitHub (pinned to 5e758547a8)