Eliminate unnecessary errors in the logs and fail gracefully if we cannot find build to reschedule. (#2401)

* Fail gracefully so we do not polute logs with stack traces.

* Add custom exception.

* Remove comments.

* Remove trailing white spaces.

* Formatting.
diff --git a/app_dart/lib/src/service/NoBuildFoundException.dart b/app_dart/lib/src/service/NoBuildFoundException.dart
new file mode 100644
index 0000000..6f107bd
--- /dev/null
+++ b/app_dart/lib/src/service/NoBuildFoundException.dart
@@ -0,0 +1,13 @@
+// Copyright 2022 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+class NoBuildFoundException implements Exception {
+  /// Create a custom exception for no build found Errors.
+  NoBuildFoundException(this.cause);
+
+  final String cause;
+
+  @override
+  String toString() => cause;
+}
diff --git a/app_dart/lib/src/service/luci_build_service.dart b/app_dart/lib/src/service/luci_build_service.dart
index e3762e3..a67e7b5 100644
--- a/app_dart/lib/src/service/luci_build_service.dart
+++ b/app_dart/lib/src/service/luci_build_service.dart
@@ -6,6 +6,7 @@
 import 'dart:convert';
 import 'dart:math';
 
+import 'package:cocoon_service/src/service/NoBuildFoundException.dart';
 import 'package:github/github.dart' as github;
 import 'package:github/hooks.dart';
 
@@ -379,31 +380,35 @@
 
     final Iterable<Build> builds = await getTryBuilds(slug, sha, checkName);
 
-    final Build build = builds.first;
+    if (builds.isNotEmpty) {
+      final Build build = builds.first;
 
-    final String prString = build.tags!['buildset']!.firstWhere((String? element) => element!.startsWith('pr/git/'))!;
-    final String cipdVersion = build.tags!['cipd_version']![0]!;
-    final int prNumber = int.parse(prString.split('/')[2]);
+      final String prString = build.tags!['buildset']!.firstWhere((String? element) => element!.startsWith('pr/git/'))!;
+      final String cipdVersion = build.tags!['cipd_version']![0]!;
+      final int prNumber = int.parse(prString.split('/')[2]);
 
-    final Map<String, dynamic> userData = <String, dynamic>{'check_run_id': githubCheckRun.id};
-    final Map<String, dynamic>? properties = build.input!.properties;
-    log.info('input ${build.input!} properties $properties');
+      final Map<String, dynamic> userData = <String, dynamic>{'check_run_id': githubCheckRun.id};
+      final Map<String, dynamic>? properties = build.input!.properties;
+      log.info('input ${build.input!} properties $properties');
 
-    final ScheduleBuildRequest scheduleBuildRequest = _createPresubmitScheduleBuild(
-      slug: slug,
-      sha: sha,
-      checkName: checkName,
-      pullRequestNumber: prNumber,
-      cipdVersion: cipdVersion,
-      properties: properties,
-      userData: userData,
-    );
+      final ScheduleBuildRequest scheduleBuildRequest = _createPresubmitScheduleBuild(
+        slug: slug,
+        sha: sha,
+        checkName: checkName,
+        pullRequestNumber: prNumber,
+        cipdVersion: cipdVersion,
+        properties: properties,
+        userData: userData,
+      );
 
-    final Build scheduleBuild = await buildBucketClient.scheduleBuild(scheduleBuildRequest);
+      final Build scheduleBuild = await buildBucketClient.scheduleBuild(scheduleBuildRequest);
 
-    final String buildUrl = 'https://ci.chromium.org/ui/b/${scheduleBuild.id}';
-    await githubChecksUtil.updateCheckRun(config, slug, githubCheckRun, detailsUrl: buildUrl);
-    return scheduleBuild;
+      final String buildUrl = 'https://ci.chromium.org/ui/b/${scheduleBuild.id}';
+      await githubChecksUtil.updateCheckRun(config, slug, githubCheckRun, detailsUrl: buildUrl);
+      return scheduleBuild;
+    } else {
+      throw NoBuildFoundException('Unable to find try build.');
+    }
   }
 
   /// Gets [Build] using its [id] and passing the additional
diff --git a/app_dart/lib/src/service/scheduler.dart b/app_dart/lib/src/service/scheduler.dart
index e2f7235..0a8a7cf 100644
--- a/app_dart/lib/src/service/scheduler.dart
+++ b/app_dart/lib/src/service/scheduler.dart
@@ -5,6 +5,7 @@
 import 'dart:math';
 import 'dart:typed_data';
 
+import 'package:cocoon_service/src/service/NoBuildFoundException.dart';
 import 'package:cocoon_service/src/service/build_status_provider.dart';
 import 'package:cocoon_service/src/service/scheduler/policy.dart';
 import 'package:gcloud/db.dart';
@@ -450,8 +451,12 @@
             success = true;
           }
         } else {
-          await luciBuildService.rescheduleUsingCheckRunEvent(checkRunEvent);
-          success = true;
+          try {
+            await luciBuildService.rescheduleUsingCheckRunEvent(checkRunEvent);
+            success = true;
+          } on NoBuildFoundException {
+            log.warning('No build found to reschedule.');
+          }
         }
 
         log.fine('CheckName: $name State: $success');
diff --git a/app_dart/test/service/luci_build_service_test.dart b/app_dart/test/service/luci_build_service_test.dart
index 284e0b7..af50931 100644
--- a/app_dart/test/service/luci_build_service_test.dart
+++ b/app_dart/test/service/luci_build_service_test.dart
@@ -12,11 +12,13 @@
 import 'package:cocoon_service/src/model/luci/buildbucket.dart';
 import 'package:cocoon_service/src/model/luci/push_message.dart' as push_message;
 import 'package:cocoon_service/src/request_handling/exceptions.dart';
+import 'package:cocoon_service/src/service/NoBuildFoundException.dart';
 import 'package:cocoon_service/src/service/config.dart';
 import 'package:cocoon_service/src/service/datastore.dart';
 import 'package:cocoon_service/src/service/logging.dart';
 import 'package:cocoon_service/src/service/luci_build_service.dart';
 import 'package:github/github.dart';
+import 'package:cocoon_service/src/model/github/checks.dart' as cocoon_checks;
 import 'package:logging/logging.dart';
 import 'package:mockito/mockito.dart';
 import 'package:test/test.dart';
@@ -28,6 +30,7 @@
 import '../src/utilities/entity_generators.dart';
 import '../src/utilities/mocks.dart';
 import '../src/utilities/push_message.dart';
+import '../src/utilities/webhook_generators.dart';
 
 void main() {
   late FakeConfig config;
@@ -533,6 +536,31 @@
       });
     });
 
+    test('reschedule using checkrun event fails gracefully', () async {
+      when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
+          .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));
+
+      when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
+        return const BatchResponse(
+          responses: <Response>[
+            Response(
+              searchBuilds: SearchBuildsResponse(
+                builds: <Build>[],
+              ),
+            )
+          ],
+        );
+      });
+
+      final pushMessage = generateCheckRunEvent(action: 'created', numberOfPullRequests: 1);
+      final Map<String, dynamic> jsonMap = json.decode(pushMessage.data!);
+      final Map<String, dynamic> jsonSubMap = json.decode(jsonMap['2']);
+      final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(jsonSubMap);
+
+      expect(() async => await service.rescheduleUsingCheckRunEvent(checkRunEvent),
+          throwsA(const TypeMatcher<NoBuildFoundException>()));
+    });
+
     test('do not create postsubmit checkrun for bringup: true target', () async {
       when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
           .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));