{"record":{"id":"c69885958f3525db","repo":"iflytek/astron-agent","slug":"locked-tenant-bootstrap-app-does-not-match-the-reserved","errorCode":null,"errorMessage":"locked tenant bootstrap app does not match the reserved tenant ID","messagePattern":"locked tenant bootstrap app does not match the reserved tenant ID","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tenant/tools/database/bootstrap_credentials.go","lineNumber":150,"sourceCode":"\t\t\"\",\n\t); err != nil {\n\t\treturn fmt.Errorf(\"ensure tenant bootstrap app failed: %w\", err)\n\t}\n\n\t// Serialize reconciliation across replicas on the reserved app row before\n\t// taking any auth-index gap locks or rotating managed credentials.\n\tvar lockedAppID string\n\tvar lockedAppDisabled sql.NullBool\n\tvar lockedAppDeleted sql.NullBool\n\tif err := transaction.QueryRowContext(\n\t\tctx,\n\t\t`SELECT app_id, is_disable, is_delete FROM tb_app WHERE app_id = ? FOR UPDATE`,\n\t\tcredentials.TenantID,\n\t).Scan(&lockedAppID, &lockedAppDisabled, &lockedAppDeleted); err != nil {\n\t\treturn fmt.Errorf(\"lock tenant bootstrap app failed: %w\", err)\n\t}\n\tif lockedAppID != credentials.TenantID {\n\t\treturn errors.New(\"locked tenant bootstrap app does not match the reserved tenant ID\")\n\t}\n\tif !lockedAppDisabled.Valid || lockedAppDisabled.Bool ||\n\t\t!lockedAppDeleted.Valid || lockedAppDeleted.Bool {\n\t\treturn errors.New(\"reserved tenant bootstrap app is disabled or deleted\")\n\t}\n\treturn nil\n}\n\nfunc findTenantBootstrapCredential(\n\tctx context.Context,\n\ttransaction bootstrapTransaction,\n\tcredentials config.TenantBootstrapCredentials,\n) (bool, error) {\n\tvar collisionOwner string\n\terr := transaction.QueryRowContext(\n\t\tctx,\n\t\t`SELECT app_id\nFROM tb_auth","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/tenant/tools/database/bootstrap_credentials.go#L132-L168","documentation":"During tenant bootstrap reconciliation, ensureAndLockTenantBootstrapApp takes a FOR UPDATE row lock on tb_app using the reserved tenant ID and asserts the locked row's app_id equals credentials.TenantID. A mismatch means the bootstrap transaction loaded a row whose app_id differs from the reserved tenant ID — an internal data inconsistency, since the query selects by app_id = tenant_id, indicating concurrent modification or schema/state corruption between lock and read.","triggerScenarios":"reconcileTenantBootstrapTransaction runs while another process deletes/recreates the app row, changing app_id between the write and the locked read, or manual DB edits moved/renamed app_id values.","commonSituations":"Concurrent bootstrap jobs on the same tenant, manual database surgery on tb_app, or a migration that rewrote app_id values while bootstrap runs.","solutions":["Re-run the bootstrap reconciliation once no concurrent bootstrap job is active","Inspect tb_app rows for the tenant ID and restore app_id = reserved tenant ID if manually altered","Serialize bootstrap runs (single reconciler / advisory lock) to prevent concurrent mutation","Check for scripts/migrations that rewrite app_id and gate them against bootstrap"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := reconcileBootstrap(ctx, creds); err != nil && strings.Contains(err.Error(), \"does not match the reserved tenant ID\") { log.Error(\"bootstrap app row mutated concurrently; re-running after quiesce\"); return retryAfterLockQuiesce(ctx, creds) }","preventionTips":["Run only one bootstrap reconciler per tenant","Never hand-edit app_id on tb_app rows","Gate migrations touching tb_app against running bootstrap jobs"],"tags":["go","mysql","bootstrap","data-integrity"],"backgroundTag":"internal-invariant-violation","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}