iflytek/astron-agent · critical
check tenant bootstrap API key ownership failed
Error message
check tenant bootstrap API key ownership failed: %w
What it means
findTenantBootstrapCredential probes tb_auth to determine who owns a bootstrap API key: a successful Scan means the key belongs to another active app (hard collision error), and any Scan error other than sql.ErrNoRows is wrapped as 'check tenant bootstrap API key ownership failed'. It guards against stealing or colliding with unmanaged credentials.
Solutions
- Read the wrapped cause: fix schema mismatches by re-running migrations so tb_auth matches the queried columns.
- Grant the bootstrap user SELECT on tb_auth; verify DSN/auth to rule out access-denied errors.
- Check MySQL error log for connection drops or lock waits during bootstrap and address the contention source.
- Retry bootstrap after transient DB issues — the reconcile transaction is safe to re-run.
Example fix
// before: schema drift causes scan failure and aborts bootstrap
err = transaction.QueryRowContext(ctx, ownershipSQL, apiKey, appID).Scan(&collisionOwner)
if !errors.Is(err, sql.ErrNoRows) {
return false, fmt.Errorf("check tenant bootstrap API key ownership failed: %w", err)
}
// after: run migrations first, and treat missing rows as the expected path
// mysql: ALTER TABLE tb_auth ADD COLUMN source ... (align schema with expectations)
if err := migrations.Apply(ctx, client); err != nil {
return fmt.Errorf("schema out of sync: %w", err)
}
err = transaction.QueryRowContext(ctx, ownershipSQL, apiKey, appID).Scan(&collisionOwner)
if errors.Is(err, sql.ErrNoRows) { /* key unmanaged: proceed to adopt */ } Defensive patterns
Strategy: validation
Validate before calling
rows, err := db.Query(`SHOW COLUMNS FROM tb_auth`) // verify required columns (api_key, app_id, source, is_delete) exist before bootstrap
Try / catch
err = tx.QueryRowContext(ctx, ownershipSQL, key, id).Scan(&owner)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
if isTransient(err) {
return retryErr
}
return fmt.Errorf("check tenant bootstrap API key ownership failed: %w", err)
} Prevention
- Run migrations before bootstrap so tb_auth always matches queried columns.
- Grant SELECT on tb_auth to the bootstrap user.
- Exclude bootstrap-managed keys from key-rotation/cleanup jobs that could race the ownership check.
When it happens
Trigger: The ownership SELECT on tb_auth errors — connection loss, privilege failure, schema mismatch, or driver scan-type mismatch — while reconciling bootstrap credentials inside the transaction.
Common situations: tb_auth schema drifted (missing source/is_delete columns) after a migration; MySQL user lacks SELECT on tb_auth; transient connection drop during bootstrap; heavy contention causing lock wait errors on tb_auth index locks.
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
- locked tenant bootstrap app does not match the reserved…
- reserved tenant bootstrap app is disabled or deleted
- tenant bootstrap API key is already assigned to another…
- tenant bootstrap API key conflicts with an unmanaged…
- ensure tenant bootstrap app failed
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/b88cd4cee9943a1d.
Report an issue: GitHub.
Appendix: source
Thrown at core/tenant/tools/database/bootstrap_credentials.go:178
ctx context.Context,
transaction bootstrapTransaction,
credentials config.TenantBootstrapCredentials,
) (bool, error) {
var collisionOwner string
err := transaction.QueryRowContext(
ctx,
`SELECT app_id
FROM tb_auth
WHERE api_key = ? AND app_id <> ? AND is_delete = 0
LIMIT 1 FOR UPDATE`,
credentials.APIKey,
credentials.TenantID,
).Scan(&collisionOwner)
if err == nil {
return false, errors.New("tenant bootstrap API key is already assigned to another active app")
}
if !errors.Is(err, sql.ErrNoRows) {
return false, fmt.Errorf("check tenant bootstrap API key ownership failed: %w", err)
}
var unmanagedSecret sql.NullString
var unmanagedIsDelete sql.NullBool
err = transaction.QueryRowContext(
ctx,
`SELECT api_secret, is_delete
FROM tb_auth
WHERE app_id = ? AND api_key = ? AND COALESCE(extend, '') <> ?
LIMIT 1 FOR UPDATE`,
credentials.TenantID,
credentials.APIKey,
tenantBootstrapManagedMarker,
).Scan(&unmanagedSecret, &unmanagedIsDelete)
if err == nil {
if !unmanagedSecret.Valid || !unmanagedIsDelete.Valid || unmanagedIsDelete.Bool ||
subtle.ConstantTimeCompare([]byte(unmanagedSecret.String), []byte(credentials.Secret)) != 1 {
return false, errors.New("tenant bootstrap API key conflicts with an unmanaged credential")View on GitHub (pinned to 5e758547a8)