{"record":{"id":"e3c769b6defc6f9d","repo":"iflytek/astron-agent","slug":"disable-published-legacy-tenant-credential-failed-w","errorCode":null,"errorMessage":"disable published legacy tenant credential failed: %w","messagePattern":"disable published legacy tenant credential failed: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tenant/tools/database/bootstrap_credentials.go","lineNumber":249,"sourceCode":"\treturn nil\n}\n\nfunc rotateTenantBootstrapCredentials(\n\tctx context.Context,\n\ttransaction bootstrapTransaction,\n\tcredentials config.TenantBootstrapCredentials,\n\tnow string,\n) error {\n\tif _, err := transaction.ExecContext(\n\t\tctx,\n\t\t`UPDATE tb_auth\nSET is_delete = 1, update_time = ?\nWHERE api_key = ? AND api_secret = ?`,\n\t\tnow,\n\t\tconfig.LegacyTenantKey,\n\t\tconfig.LegacyTenantSecret,\n\t); err != nil {\n\t\treturn fmt.Errorf(\"disable published legacy tenant credential failed: %w\", err)\n\t}\n\n\tif _, err := transaction.ExecContext(\n\t\tctx,\n\t\t`UPDATE tb_auth\nSET is_delete = 1, update_time = ?\nWHERE app_id = ? AND extend = ? AND api_key <> ?`,\n\t\tnow,\n\t\tcredentials.TenantID,\n\t\ttenantBootstrapManagedMarker,\n\t\tcredentials.APIKey,\n\t); err != nil {\n\t\treturn fmt.Errorf(\"retire previous managed tenant credential failed: %w\", err)\n\t}\n\tif _, err := transaction.ExecContext(\n\t\tctx,\n\t\t`INSERT INTO tb_auth\n  (update_time, registration_time, app_id, api_key, api_secret, source, is_delete, extend)","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/tenant/tools/database/bootstrap_credentials.go#L231-L267","documentation":"rotateTenantBootstrapCredentials wraps a failure of the first UPDATE that soft-deletes the legacy published tenant credential (is_delete=1 WHERE api_key=legacyKey AND api_secret=legacySecret) in tb_auth. The %w wrap preserves the underlying MySQL/driver error. Rotation aborts so legacy and new credentials are never in an inconsistent half-rotated state.","triggerScenarios":"transaction.ExecContext(\"UPDATE tb_auth SET is_delete=1, update_time=? WHERE api_key=? AND api_secret=?\") returns a non-nil error during credential rotation inside reconcileTenantBootstrapTransaction — deadlocks, lock wait timeout, lost connection, read-only target, or SQL syntax/schema mismatch.","commonSituations":"Legacy credential rows are numerous or heavily locked by other services during rotation; deployment points the service at a replica; connection drops mid-transaction; a botched migration removed api_key/api_secret indexes causing long scans and timeouts.","solutions":["Read the wrapped cause error from tenant logs to distinguish timeout, deadlock, or connectivity failure.","Retry the rotation — it is transactional and idempotent (soft delete matches exact key/secret pair).","Confirm the UPDATE runs on the primary DB, not a read-only replica or user lacking UPDATE privilege on tb_auth.","Check index coverage on tb_auth.api_key/api_secret to avoid full-table-scan lock escalation.","If deadlocks recur, schedule rotation outside peak traffic or serialize with an advisory lock."],"exampleFix":"// before: rotation failure bubbles up unhandled\nif err := reconcileTenantBootstrapTransaction(ctx, tx, cfg); err != nil {\n    log.Fatalf(\"rotate failed: %v\", err)\n}\n\n// after: retry with backoff before giving up\nif err := retry.OnError(wait.Backoff{Steps: 3, Duration: time.Second}, isRetryableDBError, func() error {\n    return reconcileTenantBootstrapTransaction(ctx, tx, cfg)\n}); err != nil {\n    log.Errorf(\"tenant credential rotation failed after retries: %v\", err)\n}","handlingStrategy":"retry","validationCode":"var writable int\ndb.QueryRow(\"SELECT @@read_only = 0\").Scan(&writable)\nif writable != 1 { return errors.New(\"target DB is read-only; cannot rotate credentials\") }","typeGuard":null,"tryCatchPattern":"err := rotateTenantBootstrapCredentials(ctx, tx, cfg)\nif err != nil {\n    var myErr *mysql.MySQLError\n    if errors.As(err, &myErr) && (myErr.Number == 1205 || myErr.Number == 1213) {\n        // lock wait timeout / deadlock: retry\n    }\n}","preventionTips":["Schedule rotation during low-traffic windows.","Ensure indexes on tb_auth.api_key and api_secret.","Confirm UPDATE privilege and primary routing for the service user.","Re-run rotation safely — it is idempotent."],"tags":["database","mysql","credential-rotation","go"],"backgroundTag":"database-write-failed","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}