diff --git a/src/main/java/org/eclipsefoundation/git/eca/resource/StatusResource.java b/src/main/java/org/eclipsefoundation/git/eca/resource/StatusResource.java
index 630ae1f1063cf1ba305b9b7c95eb077225559362..98fd73d3b2d6350d1e8735cc9aacfe2351026fc9 100644
--- a/src/main/java/org/eclipsefoundation/git/eca/resource/StatusResource.java
+++ b/src/main/java/org/eclipsefoundation/git/eca/resource/StatusResource.java
@@ -36,6 +36,7 @@ import org.eclipsefoundation.git.eca.namespace.ProviderType;
 import org.eclipsefoundation.git.eca.service.GithubApplicationService;
 import org.eclipsefoundation.git.eca.service.ValidationService;
 import org.eclipsefoundation.git.eca.service.ValidationStatusService;
+import org.jboss.resteasy.client.exception.ResteasyWebApplicationException;
 
 import io.quarkus.qute.Location;
 import io.quarkus.qute.Template;
@@ -49,6 +50,12 @@ import io.quarkus.qute.Template;
 @Path("eca/status")
 public class StatusResource extends GithubAdjacentResource {
 
+    // parameter names for the status error page
+    private static final String INCLUDE_INSTALL_LINK_PARAMETER = "includeInstallLink";
+    private static final String MESSAGE_PARAMETER = "message";
+    private static final String PULL_REQUEST_NUMBER_PARAMETER = "pullRequestNumber";
+    private static final String REPO_URL_PARAMETER = "repoUrl";
+
     @Inject
     GithubApplicationService ghAppService;
     @Inject
@@ -64,6 +71,8 @@ public class StatusResource extends GithubAdjacentResource {
     // Qute templates, generates UI status page
     @Location("simple_fingerprint_ui")
     Template statusUiTemplate;
+    @Location("error")
+    Template errorTemplate;
 
     /**
      * Standard endpoint for retrieving raw validation information on a historic request, using the fingerprint for lookups.
@@ -97,10 +106,10 @@ public class StatusResource extends GithubAdjacentResource {
                 .ok()
                 .entity(statusUiTemplate
                         .data("statuses", statuses)
-                        .data("pullRequestNumber", null)
+                        .data(PULL_REQUEST_NUMBER_PARAMETER, null)
                         .data("fullRepoName", null)
                         .data("project", ps.isEmpty() ? null : ps.get(0))
-                        .data("repoUrl", statuses.get(0).getRepoUrl())
+                        .data(REPO_URL_PARAMETER, statuses.get(0).getRepoUrl())
                         .data("installationId", null)
                         .render())
                 .build();
@@ -120,47 +129,89 @@ public class StatusResource extends GithubAdjacentResource {
     @Path("gh/{org}/{repoName}/{prNo}")
     public Response getCommitValidationForGithub(@PathParam("org") String org, @PathParam("repoName") String repoName,
             @PathParam("prNo") Integer prNo) {
+        // get the repo full name, used in a few lookups
         String repoFullName = org + '/' + repoName;
-        // check that the passed repo has a valid installation
-        String installationId = ghAppService.getInstallationForRepo(org, repoName);
-        if (StringUtils.isBlank(installationId)) {
-            throw new BadRequestException("Repo " + repoFullName + " requested, but does not have visible installation, returning");
-        }
-
         // generate the URL used to retrieve valid projects
         String repoUrl = GithubValidationHelper.getRepoUrl(org, repoName);
 
-        // get the data about the current request, and check that we have some validation statuses
-        ValidationRequest req = validationHelper.generateRequest(installationId, repoFullName, prNo, repoUrl);
-        List<CommitValidationStatus> statuses = validationStatus
-                .getHistoricValidationStatusByShas(wrapper, req.getCommits().stream().map(Commit::getHash).collect(Collectors.toList()));
-        // check if we have any data, and if there is none, attempt to validate the request information
-        if (statuses.isEmpty()) {
-            // run the validation for the current request
-            ValidationResponse r = validationHelper.validateIncomingRequest(wrapper, org, repoName, prNo);
-            if (r == null) {
-                throw new BadRequestException("Cannot validate request for " + repoFullName + "#" + prNo + " as it is already closed");
+        try {
+            // check that the passed repo has a valid installation
+            String installationId = ghAppService.getInstallationForRepo(org, repoName);
+            if (StringUtils.isBlank(installationId)) {
+                return Response
+                        .ok()
+                        .entity(errorTemplate
+                                .data(MESSAGE_PARAMETER, "No Github ECA app installation could be found for the current pull request.")
+                                .data(REPO_URL_PARAMETER, repoUrl)
+                                .data(PULL_REQUEST_NUMBER_PARAMETER, prNo)
+                                .data(INCLUDE_INSTALL_LINK_PARAMETER, true)
+                                .render())
+                        .build();
             }
 
-            // retrieve the status of the commits to display on the status page
-            statuses = validationStatus
-                    .getHistoricValidationStatusByShas(wrapper, r.getCommits().keySet().stream().collect(Collectors.toList()));
-        }
+            // get the data about the current request, and check that we have some validation statuses
+            ValidationRequest req = validationHelper.generateRequest(installationId, repoFullName, prNo, repoUrl);
+            List<CommitValidationStatus> statuses = validationStatus
+                    .getHistoricValidationStatusByShas(wrapper,
+                            req.getCommits().stream().map(Commit::getHash).collect(Collectors.toList()));
+            // check if we have any data, and if there is none, attempt to validate the request information
+            if (statuses.isEmpty()) {
+                // run the validation for the current request adhoc
+                ValidationResponse r = validationHelper.validateIncomingRequest(wrapper, org, repoName, prNo);
+                if (r == null) {
+                    return Response
+                            .ok()
+                            .entity(errorTemplate
+                                    .data(MESSAGE_PARAMETER,
+                                            "The currently selected PR is in a non-opened state, and will not be validated.")
+                                    .data(REPO_URL_PARAMETER, repoUrl)
+                                    .data(PULL_REQUEST_NUMBER_PARAMETER, prNo)
+                                    .data(INCLUDE_INSTALL_LINK_PARAMETER, false)
+                                    .render())
+                            .build();
+                }
+
+                // retrieve the status of the commits to display on the status page
+                statuses = validationStatus
+                        .getHistoricValidationStatusByShas(wrapper, r.getCommits().keySet().stream().collect(Collectors.toList()));
+            }
 
-        // get projects for use in queries + UI
-        List<Project> ps = projects.retrieveProjectsForRepoURL(repoUrl, ProviderType.GITHUB);
-        // render and return the status UI
-        return Response
-                .ok()
-                .entity(statusUiTemplate
-                        .data("statuses", statuses)
-                        .data("pullRequestNumber", prNo)
-                        .data("fullRepoName", repoFullName)
-                        .data("project", ps.isEmpty() ? null : ps.get(0))
-                        .data("repoUrl", repoUrl)
-                        .data("installationId", installationId)
-                        .render())
-                .build();
+            // get projects for use in queries + UI
+            List<Project> ps = projects.retrieveProjectsForRepoURL(repoUrl, ProviderType.GITHUB);
+            // render and return the status UI
+            return Response
+                    .ok()
+                    .entity(statusUiTemplate
+                            .data("statuses", statuses)
+                            .data(PULL_REQUEST_NUMBER_PARAMETER, prNo)
+                            .data("fullRepoName", repoFullName)
+                            .data("project", ps.isEmpty() ? null : ps.get(0))
+                            .data(REPO_URL_PARAMETER, repoUrl)
+                            .data("installationId", installationId)
+                            .render())
+                    .build();
+        } catch (BadRequestException e) {
+            return Response
+                    .ok()
+                    .entity(errorTemplate
+                            .data(MESSAGE_PARAMETER,
+                                    "Request made to validate content that is not eligible for validation (either closed, or with no identifiable changes to validate).")
+                            .data(REPO_URL_PARAMETER, repoUrl)
+                            .data(PULL_REQUEST_NUMBER_PARAMETER, prNo)
+                            .data(INCLUDE_INSTALL_LINK_PARAMETER, false)
+                            .render())
+                    .build();
+        } catch (NotFoundException | ResteasyWebApplicationException e) {
+            return Response
+                    .ok()
+                    .entity(errorTemplate
+                            .data(MESSAGE_PARAMETER,
+                                    "Could not find a pull request given the passed parameters, please check the URL and try again.")
+                            .data(REPO_URL_PARAMETER, repoUrl)
+                            .data(PULL_REQUEST_NUMBER_PARAMETER, prNo)
+                            .data(INCLUDE_INSTALL_LINK_PARAMETER, false)
+                            .render())
+                    .build();
+        }
     }
-
 }
diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html
new file mode 100644
index 0000000000000000000000000000000000000000..9a1c92daa6067ddebb9feac15ab96aa1bdbd6223
--- /dev/null
+++ b/src/main/resources/templates/error.html
@@ -0,0 +1,22 @@
+{#include eclipse_header}
+  {#title}Git ECA Validation{/}
+{/include}
+{#include eclipse_breadcrumb /}
+<div class="container" id="main-page-content">
+  <div id="main-content-row" class="row">
+    <section id="main-content" class="col-md-18 col-sm-16 margin-bottom-20">
+      <h1>Git ECA Validation Status</h1>
+      <p>{message}</p>
+      <p>If you think this is an error, please report a new issue to the <a href="https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/issues">Eclipse Foundation Helpdesk</a></p>
+      <h2>Helpful links</h2>
+      <ul>
+        <li><a href="{repoUrl}">Target repository</a>
+        <li><a href="{repoUrl}/{pullRequestNumber}">Pull request</a>
+        {#if includeInstallLink}
+        <li><a href="https://github.com/apps/eclipse-eca-validation/installations/select_target">Install the application</a>
+        {/if}
+      </ul>
+    </section>
+  </div>
+</div>
+{#include eclipse_footer /}
\ No newline at end of file