diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/FileTreeWalker.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/FileTreeWalker.java
new file mode 100644
index 0000000000000000000000000000000000000000..7729db4ef96d8fce05744917ebc82ece10b1db34
--- /dev/null
+++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/FileTreeWalker.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.test.packages.analyzer;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.logging.Logger;
+
+import com.oracle.truffle.r.test.packages.analyzer.detectors.DiffDetector;
+import com.oracle.truffle.r.test.packages.analyzer.detectors.InstallationProblemDetector;
+import com.oracle.truffle.r.test.packages.analyzer.detectors.RErrorDetector;
+import com.oracle.truffle.r.test.packages.analyzer.detectors.RInternalErrorDetector;
+import com.oracle.truffle.r.test.packages.analyzer.detectors.SegfaultDetector;
+import com.oracle.truffle.r.test.packages.analyzer.detectors.UnsupportedSpecializationDetector;
+import com.oracle.truffle.r.test.packages.analyzer.model.RPackage;
+import com.oracle.truffle.r.test.packages.analyzer.model.RPackageTestRun;
+import com.oracle.truffle.r.test.packages.analyzer.parser.LogFileParseException;
+import com.oracle.truffle.r.test.packages.analyzer.parser.LogFileParser;
+import com.oracle.truffle.r.test.packages.analyzer.parser.LogFileParser.LogFile;
+
+public class FileTreeWalker {
+    private static final Logger LOGGER = Logger.getLogger(FileTreeWalker.class.getName());
+
+    private Collection<LogFileParseException> parseErrors;
+
+    public Collection<RPackage> ftw(Path root, Date sinceDate, String glob) throws IOException {
+
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(root, glob)) {
+            parseErrors = new ArrayList<>();
+            Collection<RPackage> pkgs = new LinkedList<>();
+            for (Path p : stream) {
+                if (Files.isDirectory(p)) {
+                    Collection<RPackage> pkgVersions = visitPackageRoot(p, sinceDate);
+                    pkgs.addAll(pkgVersions);
+                }
+            }
+            return pkgs;
+        }
+    }
+
+    protected Collection<RPackage> visitPackageRoot(Path pkgRoot, Date sinceDate) throws IOException {
+        String pkgName = pkgRoot.getFileName().toString();
+
+        Collection<RPackage> pkgs = new LinkedList<>();
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(pkgRoot)) {
+            for (Path p : stream) {
+                if (Files.isDirectory(p)) {
+                    pkgs.add(visitPackageVersion(p, pkgName, sinceDate));
+                }
+            }
+        }
+        return pkgs;
+    }
+
+    protected RPackage visitPackageVersion(Path pkgVersionDir, String pkgName, Date sinceDate) {
+        String pkgVersion = pkgVersionDir.getFileName().toString();
+        RPackage pkg = new RPackage(pkgName, pkgVersion);
+        LOGGER.info("Found package " + pkg);
+
+        Collection<RPackageTestRun> runs = new LinkedList<>();
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(pkgVersionDir)) {
+            for (Path p : stream) {
+                if (Files.isDirectory(p)) {
+                    RPackageTestRun testRun = visitTestRun(p, pkg, sinceDate);
+                    if (testRun != null) {
+                        runs.add(testRun);
+                    }
+                }
+            }
+            pkg.setTestRuns(runs);
+        } catch (IOException e) {
+            LOGGER.severe("Error while reading package root of \"" + pkgName + "\"");
+        }
+
+        return pkg;
+    }
+
+    protected RPackageTestRun visitTestRun(Path testRunDir, RPackage pkg, Date sinceDate) {
+        int testRun = Integer.parseInt(testRunDir.getFileName().toString());
+        LOGGER.info("Visiting test run " + testRun + " of package " + pkg);
+        try {
+            RPackageTestRun pkgTestRun = new RPackageTestRun(pkg, testRun);
+            Path logFile = testRunDir.resolve(pkg.getName() + ".log");
+            FileTime lastModifiedTime = Files.getLastModifiedTime(logFile);
+            if (isNewerThan(lastModifiedTime, sinceDate)) {
+                Collection<Problem> problems = parseLogFile(logFile, pkgTestRun);
+                pkgTestRun.setProblems(problems);
+                return pkgTestRun;
+            } else {
+                LOGGER.info(String.format("Skipping package test run %s because it is too old (%s must be newer than %s)", pkgTestRun, lastModifiedTime, sinceDate));
+            }
+        } catch (IOException e) {
+            LOGGER.severe(String.format("Error while parsing test run %d of package \"%s-%s\": %s", testRun,
+                            pkg.getName(), pkg.getVersion(), e.getMessage()));
+        } catch (LogFileParseException e) {
+            LOGGER.severe(String.format("Error while parsing test run %d of package \"%s-%s\": %s", testRun,
+                            pkg.getName(), pkg.getVersion(), e.getMessage()));
+            parseErrors.add(e);
+        }
+        return null;
+    }
+
+    private static boolean isNewerThan(FileTime lastModifiedTime, Date sinceDate) {
+        Date lastModDate = new Date(lastModifiedTime.toMillis());
+        return sinceDate.compareTo(lastModDate) <= 0;
+    }
+
+    private static Collection<Problem> parseLogFile(Path logFile, RPackageTestRun pkgTestRun) throws IOException {
+        LOGGER.info("Parsing log file " + logFile);
+
+        LogFileParser lfParser = new LogFileParser(logFile, pkgTestRun);
+        lfParser.addDetector(LogFileParser.Token.BEGIN_SUGGESTS_INSTALL, InstallationProblemDetector.INSTANCE);
+        lfParser.addDetector(SegfaultDetector.INSTANCE);
+        lfParser.addDetector(RErrorDetector.INSTANCE);
+        lfParser.addDetector(UnsupportedSpecializationDetector.INSTANCE);
+        lfParser.addDetector(RInternalErrorDetector.INSTANCE);
+        lfParser.addTestResultDetector(DiffDetector.INSTANCE);
+
+        LogFile parseLogFile = lfParser.parseLogFile();
+        Collection<Problem> problems = parseLogFile.collectProblems();
+        pkgTestRun.setSuccess(parseLogFile.isSuccess());
+
+        // log problems
+        LOGGER.fine("Overall test result: " + (pkgTestRun.isSuccess() ? "OK" : "FAILED"));
+        for (Problem problem : problems) {
+            LOGGER.fine(problem.toString());
+        }
+
+        return problems;
+    }
+
+    public Collection<LogFileParseException> getParseErrors() {
+        return parseErrors;
+    }
+
+}
diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/PTAMain.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/PTAMain.java
index 8a0a14b27aa2f98b4fec61d494bb65a8831559f7..7342253b38bd39285155e7eb39b5ecf797a9033a 100644
--- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/PTAMain.java
+++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/PTAMain.java
@@ -23,11 +23,8 @@
 package com.oracle.truffle.r.test.packages.analyzer;
 
 import java.io.IOException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.nio.file.attribute.FileTime;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
@@ -36,7 +33,6 @@ import java.util.Collection;
 import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.Map;
 import java.util.logging.ConsoleHandler;
 import java.util.logging.FileHandler;
@@ -47,18 +43,8 @@ import java.util.logging.SimpleFormatter;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import com.oracle.truffle.r.test.packages.analyzer.detectors.DiffDetector;
-import com.oracle.truffle.r.test.packages.analyzer.detectors.InstallationProblemDetector;
-import com.oracle.truffle.r.test.packages.analyzer.detectors.RErrorDetector;
-import com.oracle.truffle.r.test.packages.analyzer.detectors.RInternalErrorDetector;
-import com.oracle.truffle.r.test.packages.analyzer.detectors.SegfaultDetector;
-import com.oracle.truffle.r.test.packages.analyzer.detectors.UnsupportedSpecializationDetector;
 import com.oracle.truffle.r.test.packages.analyzer.dump.HTMLDumper;
 import com.oracle.truffle.r.test.packages.analyzer.model.RPackage;
-import com.oracle.truffle.r.test.packages.analyzer.model.RPackageTestRun;
-import com.oracle.truffle.r.test.packages.analyzer.parser.LogFileParseException;
-import com.oracle.truffle.r.test.packages.analyzer.parser.LogFileParser;
-import com.oracle.truffle.r.test.packages.analyzer.parser.LogFileParser.LogFile;
 
 /**
  * Main class of the package analysis tool.<br>
@@ -108,6 +94,29 @@ public class PTAMain {
         ftw(Paths.get(remainingArgs[0]), outDir, sinceDate, parser.get("glob"));
     }
 
+    private static void ftw(Path root, Path outDir, Date sinceDate, String glob) {
+        HTMLDumper htmlDumper = new HTMLDumper(outDir);
+
+        // fail early
+        try {
+            if (!htmlDumper.createAndCheckOutDir()) {
+                LOGGER.severe("Cannot write to output directory: " + outDir);
+                System.exit(1);
+            }
+        } catch (IOException e) {
+            LOGGER.severe(String.format("Cannot create output directory: %s ", e.getMessage()));
+            System.exit(1);
+        }
+
+        try {
+            FileTreeWalker walker = new FileTreeWalker();
+            Collection<RPackage> pkgs = walker.ftw(root, sinceDate, glob);
+            htmlDumper.dump(pkgs, walker.getParseErrors());
+        } catch (IOException e) {
+            LOGGER.severe("Error while traversing package test directory: " + e.getMessage());
+        }
+    }
+
     private static final Pattern REL_SINCE_PATTERN = Pattern.compile("last(\\d+)(days?|weeks?|months?)");
 
     private static Date parseSinceDate(OptionsParser parser) {
@@ -176,121 +185,6 @@ public class PTAMain {
 
     private static final String LF = System.lineSeparator();
 
-    private static void ftw(Path root, Path outDir, Date sinceDate, String glob) throws IOException {
-        // TODO FS checking
-
-        HTMLDumper htmlDumper = new HTMLDumper(outDir);
-
-        // fail early
-        try {
-            if (!htmlDumper.createAndCheckOutDir()) {
-                LOGGER.severe("Cannot write to output directory: " + outDir);
-                System.exit(1);
-            }
-        } catch (IOException e) {
-            LOGGER.severe(String.format("Cannot create output directory: %s ", e.getMessage()));
-            System.exit(1);
-        }
-
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(root, glob)) {
-            Collection<RPackage> pkgs = new LinkedList<>();
-            for (Path p : stream) {
-                if (Files.isDirectory(p)) {
-                    Collection<RPackage> pkgVersions = visitPackageRoot(p, sinceDate);
-                    pkgs.addAll(pkgVersions);
-                }
-            }
-            htmlDumper.dump(pkgs);
-        }
-    }
-
-    private static Collection<RPackage> visitPackageRoot(Path pkgRoot, Date sinceDate) throws IOException {
-        String pkgName = pkgRoot.getFileName().toString();
-
-        Collection<RPackage> pkgs = new LinkedList<>();
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(pkgRoot)) {
-            for (Path p : stream) {
-                if (Files.isDirectory(p)) {
-                    pkgs.add(visitPackageVersion(p, pkgName, sinceDate));
-                }
-            }
-        }
-        return pkgs;
-    }
-
-    private static RPackage visitPackageVersion(Path pkgVersionDir, String pkgName, Date sinceDate) {
-        String pkgVersion = pkgVersionDir.getFileName().toString();
-        RPackage pkg = new RPackage(pkgName, pkgVersion);
-        LOGGER.info("Found package " + pkg);
-
-        Collection<RPackageTestRun> runs = new LinkedList<>();
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(pkgVersionDir)) {
-            for (Path p : stream) {
-                if (Files.isDirectory(p)) {
-                    RPackageTestRun testRun = visitTestRun(p, pkg, sinceDate);
-                    if (testRun != null) {
-                        runs.add(testRun);
-                    }
-                }
-            }
-            pkg.setTestRuns(runs);
-        } catch (IOException e) {
-            LOGGER.severe("Error while reading package root of \"" + pkgName + "\"");
-        }
-
-        return pkg;
-    }
-
-    private static RPackageTestRun visitTestRun(Path testRunDir, RPackage pkg, Date sinceDate) {
-        int testRun = Integer.parseInt(testRunDir.getFileName().toString());
-        LOGGER.info("Visiting test run " + testRun + " of package " + pkg);
-        try {
-            RPackageTestRun pkgTestRun = new RPackageTestRun(pkg, testRun);
-            Path logFile = testRunDir.resolve(pkg.getName() + ".log");
-            FileTime lastModifiedTime = Files.getLastModifiedTime(logFile);
-            if (isNewerThan(lastModifiedTime, sinceDate)) {
-                Collection<Problem> problems = parseLogFile(logFile, pkgTestRun);
-                pkgTestRun.setProblems(problems);
-                return pkgTestRun;
-            } else {
-                LOGGER.info(String.format("Skipping package test run %s because it is too old (%s must be newer than %s)", pkgTestRun, lastModifiedTime, sinceDate));
-            }
-        } catch (IOException | LogFileParseException e) {
-            LOGGER.severe(String.format("Error while parsing test run %d of package \"%s-%s\": %s", testRun,
-                            pkg.getName(), pkg.getVersion(), e.getMessage()));
-        }
-        return null;
-    }
-
-    private static boolean isNewerThan(FileTime lastModifiedTime, Date sinceDate) {
-        Date lastModDate = new Date(lastModifiedTime.toMillis());
-        return sinceDate.compareTo(lastModDate) <= 0;
-    }
-
-    private static Collection<Problem> parseLogFile(Path logFile, RPackageTestRun pkgTestRun) throws IOException {
-        LOGGER.info("Parsing log file " + logFile);
-
-        LogFileParser lfParser = new LogFileParser(logFile, pkgTestRun);
-        lfParser.addDetector(LogFileParser.Token.BEGIN_SUGGESTS_INSTALL, InstallationProblemDetector.INSTANCE);
-        lfParser.addDetector(SegfaultDetector.INSTANCE);
-        lfParser.addDetector(RErrorDetector.INSTANCE);
-        lfParser.addDetector(UnsupportedSpecializationDetector.INSTANCE);
-        lfParser.addDetector(RInternalErrorDetector.INSTANCE);
-        lfParser.addTestResultDetector(DiffDetector.INSTANCE);
-
-        LogFile parseLogFile = lfParser.parseLogFile();
-        Collection<Problem> problems = parseLogFile.collectProblems();
-        pkgTestRun.setSuccess(parseLogFile.isSuccess());
-
-        // log problems
-        LOGGER.fine("Overall test result: " + (pkgTestRun.isSuccess() ? "OK" : "FAILED"));
-        for (Problem problem : problems) {
-            LOGGER.fine(problem.toString());
-        }
-
-        return problems;
-    }
-
     private static void printHelpAndExit() {
         StringBuilder sb = new StringBuilder();
         sb.append("USAGE: ").append(PTAMain.class.getSimpleName()).append(" [OPTIONS] ROOT").append(LF);
diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/detectors/RErrorDetector.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/detectors/RErrorDetector.java
index 71c5764933983d98633dc5489463cbd4c3d0c1af..7b42a3b131f6b7c6a1419f5749f106b56ac01061 100644
--- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/detectors/RErrorDetector.java
+++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/detectors/RErrorDetector.java
@@ -37,7 +37,7 @@ public class RErrorDetector extends LineDetector {
 
     public static final RErrorDetector INSTANCE = new RErrorDetector();
 
-    private static final Pattern PATTERN = Pattern.compile(".*\\wError(?<CALLSTR>.*)??: (?<MSG>.*)");
+    private static final Pattern PATTERN = Pattern.compile("(.*\\s)?Error( in (?<CALLSTR>\\S*) )?: (?<MSG>.*)");
 
     protected RErrorDetector() {
     }
@@ -63,7 +63,7 @@ public class RErrorDetector extends LineDetector {
             if (matcher.matches()) {
                 String callString = matcher.group("CALLSTR");
                 String message = matcher.group("MSG");
-                if (message.trim().isEmpty()) {
+                if (message.trim().isEmpty() && it.hasNext()) {
                     // message could be in the next line
                     message = it.next();
                     ++i;
@@ -94,9 +94,9 @@ public class RErrorDetector extends LineDetector {
         @Override
         public String getSummary() {
             if (callString != null) {
-                return "RError in '" + callString + "'";
+                return "Error in " + callString + "";
             }
-            return "RError";
+            return "Error";
         }
 
         @Override
diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/AbstractDumper.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/AbstractDumper.java
index 676c36a71f87df0d902746fe853b7c99ce392e4e..ff4ef7de99fc87760a21bca94bc92b85dbc588cc 100644
--- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/AbstractDumper.java
+++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/AbstractDumper.java
@@ -24,7 +24,6 @@ package com.oracle.truffle.r.test.packages.analyzer.dump;
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -32,10 +31,11 @@ import java.util.stream.Collectors;
 import com.oracle.truffle.r.test.packages.analyzer.Problem;
 import com.oracle.truffle.r.test.packages.analyzer.model.RPackage;
 import com.oracle.truffle.r.test.packages.analyzer.model.RPackageTestRun;
+import com.oracle.truffle.r.test.packages.analyzer.parser.LogFileParseException;
 
 public abstract class AbstractDumper {
 
-    public abstract void dump(Collection<RPackage> problems);
+    public abstract void dump(Collection<RPackage> problems, Collection<LogFileParseException> collection);
 
     protected Map<Class<? extends Problem>, List<Problem>> groupByType(Collection<Problem> problems) {
         return problems.stream().collect(Collectors.groupingBy(p -> p.getClass()));
diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/HTMLDumper.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/HTMLDumper.java
index 853bb07cbb65c51d3c048bf643f065a919e9a4b3..fa861b8245470de76f660af89381765ff897a255 100644
--- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/HTMLDumper.java
+++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/HTMLDumper.java
@@ -43,6 +43,7 @@ import com.oracle.truffle.r.test.packages.analyzer.Problem;
 import com.oracle.truffle.r.test.packages.analyzer.dump.HTMLBuilder.Tag;
 import com.oracle.truffle.r.test.packages.analyzer.model.RPackage;
 import com.oracle.truffle.r.test.packages.analyzer.model.RPackageTestRun;
+import com.oracle.truffle.r.test.packages.analyzer.parser.LogFileParseException;
 
 public class HTMLDumper extends AbstractDumper {
 
@@ -65,16 +66,16 @@ public class HTMLDumper extends AbstractDumper {
     }
 
     @Override
-    public void dump(Collection<RPackage> problems) {
+    public void dump(Collection<RPackage> problems, Collection<LogFileParseException> parseErrors) {
         try {
             createAndCheckOutDir();
-            dumpIndexFile(problems);
+            dumpIndexFile(problems, parseErrors);
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 
-    private void dumpIndexFile(Collection<RPackage> problems) {
+    private void dumpIndexFile(Collection<RPackage> problems, Collection<LogFileParseException> parseErrors) {
         Path indexFile = destDir.resolve("index.html");
 
         try (BufferedWriter bw = Files.newBufferedWriter(indexFile, CREATE, TRUNCATE_EXISTING, WRITE)) {
@@ -86,13 +87,14 @@ public class HTMLDumper extends AbstractDumper {
             Tag errorDistributionTable = generateTypeDistributionTable(builder, groupByType(allProblems));
             Tag pkgDistributionTable = generateTestRunDistributionTable(builder, groupByTestRuns(allTestRuns, allProblems));
             Tag distrinctProblemDistributionTable = generateDistinctProblemDistribution(builder, groupByProblemContent(allProblems));
+            Tag parseErrorsList = generateParseErrorsList(builder, parseErrors);
 
             builder.html(builder.head(builder.title(TITLE)), builder.body(
-                            builder.h1(TITLE),
-                            builder.p("Numer of analyzer package test runs: " + allTestRuns.size()),
+                            builder.h1(TITLE), builder.p("Numer of analyzed package test runs: " + allTestRuns.size()),
                             builder.h2("Distribution by Problem Type"), errorDistributionTable,
                             builder.h2("Distribution by Package Test Run"), pkgDistributionTable,
-                            builder.h2("Distinct Problem Distribution"), distrinctProblemDistributionTable));
+                            builder.h2("Distinct Problem Distribution"), distrinctProblemDistributionTable,
+                            builder.h2("Failed Analysis Runs"), parseErrorsList));
             builder.dump();
         } catch (IOException e) {
             e.printStackTrace();
@@ -212,4 +214,20 @@ public class HTMLDumper extends AbstractDumper {
         return table;
     }
 
+    private static Tag generateParseErrorsList(HTMLBuilder builder, Collection<LogFileParseException> parseErrors) {
+
+        Tag table = builder.table(builder.tr(
+                        builder.th("Package Test Run"),
+                        builder.th("Location"),
+                        builder.th("Message")));
+
+        for (LogFileParseException e : parseErrors) {
+            table.addChild(builder.tr(
+                            builder.td(e.getTestRun().toString()),
+                            builder.td(e.getLocation().toString()),
+                            builder.td(e.getMessage())));
+        }
+        return table;
+    }
+
 }
diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/DiffParser.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/DiffParser.java
index 95739783f6451be275c17239e29885a370cc3d81..7c6a3ab3ec13100b4bf7753afbd8fe3237ce6b96 100644
--- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/DiffParser.java
+++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/DiffParser.java
@@ -48,7 +48,7 @@ public class DiffParser {
      * group("CMD") = command<br>
      * </p>
      */
-    static final Pattern CHANGE_CMD_PATTERN = Pattern.compile("(\\d+)(,(\\d+))?(?<CMD>a|c|d)(\\d+)(,(\\d+))?");
+    public static final Pattern CHANGE_CMD_PATTERN = Pattern.compile("(\\d+)(,(\\d+))?(?<CMD>a|c|d)(\\d+)(,(\\d+))?");
 
     protected DiffParser(LogFileParser parent) {
         this.parent = parent;
@@ -90,7 +90,7 @@ public class DiffParser {
                         break;
                     }
                     default:
-                        throw new LogFileParseException("Unknown diff command: ");
+                        throw parent.parseError("Unknown diff command: ");
                 }
             } else {
                 // no more diff chunks; exit loop
@@ -122,7 +122,7 @@ public class DiffParser {
         if (matcher.matches()) {
             String cmdStr = matcher.group("CMD");
             if (cmdStr.length() != 1) {
-                throw new LogFileParseException("Invalid diff change command: " + cmdStr);
+                throw parent.parseError("Invalid diff change command: " + cmdStr);
             }
 
             char cmd = cmdStr.charAt(0);
@@ -134,7 +134,7 @@ public class DiffParser {
 
             return new ChangeCommand(atoi(lFromStr), atoi(lToStr), cmd, atoi(rFromStr), atoi(rToStr));
         }
-        throw new LogFileParseException("Invalid diff change command: " + parent.getCurLine().text);
+        throw parent.parseError("Invalid diff change command: " + parent.getCurLine().text);
     }
 
     private static int atoi(String rToStr) {
@@ -224,7 +224,7 @@ public class DiffParser {
         public final int rFrom;
         public final int rTo;
 
-        protected ChangeCommand(int lFrom, int lTo, char cmd, int rFrom, int rTo) {
+        public ChangeCommand(int lFrom, int lTo, char cmd, int rFrom, int rTo) {
             this.lFrom = lFrom;
             this.lTo = lTo;
             this.cmd = cmd;
diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParseException.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParseException.java
index ac9740109bb06bc0f6b9c7419eb8b7fd998ba537..c76f3756a2ed2d98f24c1c3ee933673c072e3193 100644
--- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParseException.java
+++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParseException.java
@@ -22,11 +22,29 @@
  */
 package com.oracle.truffle.r.test.packages.analyzer.parser;
 
+import java.util.Objects;
+
+import com.oracle.truffle.r.test.packages.analyzer.Location;
+import com.oracle.truffle.r.test.packages.analyzer.model.RPackageTestRun;
+
 @SuppressWarnings("serial")
 public class LogFileParseException extends RuntimeException {
 
-    protected LogFileParseException(String message) {
+    private final RPackageTestRun testRun;
+    private final Location location;
+
+    protected LogFileParseException(String message, RPackageTestRun testRun, Location location) {
         super(message);
+        this.testRun = Objects.requireNonNull(testRun);
+        this.location = Objects.requireNonNull(location);
+    }
+
+    public RPackageTestRun getTestRun() {
+        return testRun;
+    }
+
+    public Location getLocation() {
+        return location;
     }
 
 }
diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParser.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParser.java
index 4f7a2580548c497d97a2979009222ff5b2c6690c..64915677edad06704543e27e73b8fb06d5754bfd 100644
--- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParser.java
+++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParser.java
@@ -113,7 +113,7 @@ public class LogFileParser {
     void expectEOF() throws IOException {
         consumeLine();
         if (!isEOF(curLine)) {
-            throw new LogFileParseException("Expected end of file but was " + curLine.text);
+            throw new LogFileParseException("Expected end of file but was " + curLine.text, pkg, getCurrentLocation());
         }
     }
 
@@ -153,14 +153,14 @@ public class LogFileParser {
             }
             checkResults.setSuccess(false);
         } else {
-            throw new LogFileParseException("Unexpected checking message: " + curLine.text);
+            throw parseError("Unexpected checking message: " + curLine.text);
         }
         expect(Token.END_CHECKING);
 
         return checkResults;
     }
 
-    private Location getCurrentLocation() {
+    Location getCurrentLocation() {
         return new Location(logFile.path, curLine.lineNr);
     }
 
@@ -171,7 +171,7 @@ public class LogFileParser {
 
         String mode = trim(curLine.text).substring(Token.BEGIN_INSTALL_TEST.linePrefix.length());
         if (!("FastR".equals(mode) || "GnuR".equals(mode))) {
-            throw new LogFileParseException("Invalid mode: " + mode);
+            throw parseError("Invalid mode: " + mode);
         }
 
         Section installationTask = parseInstallationTask();
@@ -301,13 +301,13 @@ public class LogFileParser {
         return pkg.getPackage().getName();
     }
 
-    private static boolean parseStatus(String substring) {
+    private boolean parseStatus(String substring) {
         if (Token.OK.linePrefix.equals(substring.trim())) {
             return true;
         } else if (Token.FAILED.linePrefix.equals(substring.trim())) {
             return false;
         }
-        throw new LogFileParseException("Unexpected status: " + substring);
+        throw parseError("Unexpected status: " + substring);
     }
 
     private Section parseInstallSuggests() throws IOException {
@@ -409,10 +409,14 @@ public class LogFileParser {
     void expect(String linePrefix) throws IOException {
         consumeLine();
         if (isEOF(curLine) || !trim(curLine.text).startsWith(linePrefix)) {
-            throw new LogFileParseException("Unexpected line " + (curLine.lineNr + 1) + " (expected \"" + linePrefix + "\" but was \"" + (isEOF(curLine) ? "<EOF>" : curLine.text) + "\"");
+            throw parseError("Unexpected line " + (curLine.lineNr + 1) + " (expected \"" + linePrefix + "\" but was \"" + (isEOF(curLine) ? "<EOF>" : curLine.text) + "\"");
         }
     }
 
+    LogFileParseException parseError(String message) {
+        throw new LogFileParseException(message, pkg, getCurrentLocation());
+    }
+
     void consumeLine() throws IOException {
         curLine = la;
         // skip empty lines
diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/PatternTest.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/test/PatternTest.java
similarity index 96%
rename from com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/PatternTest.java
rename to com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/test/PatternTest.java
index f4c8cce61213940de30211bc41871767ff86b1f0..3084ee7d470bb57e8be9641eac558a0c21eaff4f 100644
--- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/PatternTest.java
+++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/test/PatternTest.java
@@ -20,13 +20,14 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.test.packages.analyzer.parser;
+package com.oracle.truffle.r.test.packages.analyzer.test;
 
 import java.util.regex.Matcher;
 
 import org.junit.Assert;
 import org.junit.Test;
 
+import com.oracle.truffle.r.test.packages.analyzer.parser.DiffParser;
 import com.oracle.truffle.r.test.packages.analyzer.parser.DiffParser.ChangeCommand;
 
 public class PatternTest {
diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/test/RErrorDetectorTest.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/test/RErrorDetectorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cf432fc3f5c48399a45195a7b0770aa119f03989
--- /dev/null
+++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/test/RErrorDetectorTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.test.packages.analyzer.test;
+
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.packages.analyzer.Location;
+import com.oracle.truffle.r.test.packages.analyzer.Problem;
+import com.oracle.truffle.r.test.packages.analyzer.detectors.RErrorDetector;
+import com.oracle.truffle.r.test.packages.analyzer.model.RPackage;
+import com.oracle.truffle.r.test.packages.analyzer.model.RPackageTestRun;
+
+public class RErrorDetectorTest {
+
+    private static final RPackage pkg;
+    private static final RPackageTestRun pkgTestRun;
+
+    static {
+        pkg = new RPackage("fastr", "0.27");
+        pkg.setLocation(Paths.get("fastr"));
+        pkgTestRun = new RPackageTestRun(pkg, 1);
+    }
+
+    @Test
+    public void testMultiLine() {
+        List<String> lines = Arrays.asList(new String[]{"Error in check(options) : ",
+                        "ERROR: installing Rd objects failed for package ‘RUnit’"});
+
+        Collection<Problem> detect = RErrorDetector.INSTANCE.detect(pkgTestRun, loc(), lines);
+        Assert.assertEquals(1, detect.size());
+
+        Optional<Problem> findFirst = detect.stream().findFirst();
+        Assert.assertEquals("Error in check(options)", findFirst.orElse(null).getSummary().trim());
+        Assert.assertEquals("ERROR: installing Rd objects failed for package ‘RUnit’", findFirst.orElse(null).getDetails().trim());
+
+    }
+
+    @Test
+    public void testSingleLine0() {
+        List<String> lines = Collections.singletonList("Error in check(options) : invalid value for 'label' ");
+
+        Collection<Problem> detect = RErrorDetector.INSTANCE.detect(pkgTestRun, loc(), lines);
+        Assert.assertEquals(1, detect.size());
+
+        Optional<Problem> findFirst = detect.stream().findFirst();
+        Assert.assertEquals("Error in check(options)", findFirst.orElse(null).getSummary().trim());
+        Assert.assertEquals("invalid value for 'label'", findFirst.orElse(null).getDetails().trim());
+
+    }
+
+    @Test
+    public void testSingleLine1() {
+        List<String> lines = Collections.singletonList("Error in check : invalid value for 'label' ");
+
+        Collection<Problem> detect = RErrorDetector.INSTANCE.detect(pkgTestRun, loc(), lines);
+        Assert.assertEquals(1, detect.size());
+
+        Optional<Problem> findFirst = detect.stream().findFirst();
+        Assert.assertEquals("Error in check", findFirst.orElse(null).getSummary().trim());
+        Assert.assertEquals("invalid value for 'label'", findFirst.orElse(null).getDetails().trim());
+
+    }
+
+    @Test
+    public void testSingleLineMultipleColon() {
+        List<String> lines = Collections.singletonList("Error in check(options) : invalid value for 'label' : ");
+
+        Collection<Problem> detect = RErrorDetector.INSTANCE.detect(pkgTestRun, loc(), lines);
+        Assert.assertEquals(1, detect.size());
+
+        Optional<Problem> findFirst = detect.stream().findFirst();
+        Assert.assertEquals("Error in check(options)", findFirst.orElse(null).getSummary().trim());
+        Assert.assertEquals("invalid value for 'label' :", findFirst.orElse(null).getDetails().trim());
+
+    }
+
+    @Test
+    public void testSingleLineWithoutCallstring0() {
+        List<String> lines = Collections.singletonList("Error: invalid value for 'label' ");
+
+        Collection<Problem> detect = RErrorDetector.INSTANCE.detect(pkgTestRun, loc(), lines);
+        Assert.assertEquals(1, detect.size());
+
+        Optional<Problem> findFirst = detect.stream().findFirst();
+        Assert.assertEquals("Error", findFirst.orElse(null).getSummary().trim());
+        Assert.assertEquals("invalid value for 'label'", findFirst.orElse(null).getDetails().trim());
+
+    }
+
+    @Test
+    public void testSingleLineWithoutCallstring1() {
+        List<String> lines = Collections.singletonList("Error: invalid value for 'label' : ");
+
+        Collection<Problem> detect = RErrorDetector.INSTANCE.detect(pkgTestRun, loc(), lines);
+        Assert.assertEquals(1, detect.size());
+
+        Optional<Problem> findFirst = detect.stream().findFirst();
+        Assert.assertEquals("Error", findFirst.orElse(null).getSummary().trim());
+        Assert.assertEquals("invalid value for 'label' :", findFirst.orElse(null).getDetails().trim());
+    }
+
+    @Test
+    public void testRInternalError0() {
+        List<String> lines = Collections.singletonList("RInternalError: invalid value for 'label'");
+
+        Collection<Problem> detect = RErrorDetector.INSTANCE.detect(pkgTestRun, loc(), lines);
+        Assert.assertEquals(0, detect.size());
+    }
+
+    @Test
+    public void testRInternalError1() {
+        List<String> lines = Collections.singletonList("> RInternalError: invalid value for 'label'");
+
+        Collection<Problem> detect = RErrorDetector.INSTANCE.detect(pkgTestRun, loc(), lines);
+        Assert.assertEquals(0, detect.size());
+    }
+
+    @Test
+    public void testRInternalError2() {
+        List<String> lines = Collections.singletonList("  RInternalError: invalid value for 'label'");
+
+        Collection<Problem> detect = RErrorDetector.INSTANCE.detect(pkgTestRun, loc(), lines);
+        Assert.assertEquals(0, detect.size());
+    }
+
+    @Test
+    public void testWithLinePrefix() {
+        List<String> lines = Collections.singletonList("> Error: invalid value for 'label'");
+
+        Collection<Problem> detect = RErrorDetector.INSTANCE.detect(pkgTestRun, loc(), lines);
+        Assert.assertEquals(1, detect.size());
+
+        Optional<Problem> findFirst = detect.stream().findFirst();
+        Assert.assertEquals("Error", findFirst.orElse(null).getSummary().trim());
+        Assert.assertEquals("invalid value for 'label'", findFirst.orElse(null).getDetails().trim());
+    }
+
+    private static Location loc() {
+        return new Location(pkg.getLocation(), 0);
+    }
+
+}