diff --git a/html/ops/error_summary.php b/html/ops/error_summary.php
new file mode 100644
index 0000000000..5aee8d491a
--- /dev/null
+++ b/html/ops/error_summary.php
@@ -0,0 +1,63 @@
+.
+
+// generate a list of the most common exit codes
+// and links to N examples of each
+
+require_once("../inc/util.inc");
+require_once("../inc/result.inc");
+
+function compare ($x, $y) {
+ return $x->count < $y->count;
+}
+
+$t = time() - 7*86400;
+$results = BoincResult::enum_fields("id, exit_status", "server_state=5 and outcome=3 and exit_status<>0 and received_time>$t", "");
+$error_codes = array();
+foreach ($results as $r) {
+ $e = $r->exit_status;
+ if (array_key_exists($e, $error_codes)) {
+ $x = $error_codes[$e];
+ $x->count++;
+ $x->results[] = $r;
+ $error_codes[$e] = $x;
+ } else {
+ $x = new StdClass;
+ $x->count = 0;
+ $x->results = array($r);
+ $error_codes[$e] = $x;
+ }
+}
+
+uasort($error_codes, 'compare');
+
+page_head("Error summary");
+$i = 0;
+foreach ($error_codes as $code => $x) {
+ if ($i++ > 10) break;
+ echo "
Exit status".exit_status_string($code)." ($x->count results)\n";
+ $results = $x->results;
+ $j = 0;
+ foreach ($results as $r) {
+ if ($j++ > 10) break;
+ echo "
id>$r->id\n";
+ }
+}
+page_tail();
+
+?>