diff --git a/release_scripts/APIUpdate/APIUpdate-1.0-jar-with-dependencies.jar b/release_scripts/APIUpdate/APIUpdate-1.0-jar-with-dependencies.jar
index dc7ab7992aea6c551c9e1c4eb1ae72eb234570f8..6d330d16936409b799dccb1f1917a0bb52bc8614 100644
Binary files a/release_scripts/APIUpdate/APIUpdate-1.0-jar-with-dependencies.jar and b/release_scripts/APIUpdate/APIUpdate-1.0-jar-with-dependencies.jar differ
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java
index 3b59850ac952a7923846a7eb8c35c92efa52005f..42a89f8e221eedd5db41942500d45d34bc2b7f6e 100644
--- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java
@@ -40,12 +40,17 @@
 import java.io.FileFilter;
 import java.io.IOException;
 import java.text.MessageFormat;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javassist.CtBehavior;
@@ -53,11 +58,14 @@
 import javassist.CtField;
 import javassist.CtMember;
 import javassist.Modifier;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 
 /**
  * Handles diffing the public API between two jar files.
  */
 public class APIDiff {
+    private static final Logger LOGGER = Logger.getLogger(APIDiff.class.getName());
 
     // filters to a jar or nbm file
     private static final FileFilter JAR_FILTER
@@ -67,19 +75,29 @@ public class APIDiff {
      * Identifies common jar files between two directories. Only files listed in
      * the directory are considered. This method does not recurse.
      *
-     * @param prevDir The previous version directory.
-     * @param currDir The current version directory.
+     * @param prev The previous version directory.
+     * @param curr The current version directory.
      * @return The jar file names.
      */
-    static List<String> getCommonJars(File prevDir, File currDir) {
-        Set<String> prevJars = getJars(prevDir);
-        Set<String> currJars = getJars(currDir);
-
-        Set<String> commonJars = new HashSet<>(prevJars);
-        commonJars.retainAll(currJars);
-
-        // TODO how to handle different
-        return commonJars.stream().sorted().collect(Collectors.toList());
+    static List<Pair<File, File>> getCommonJars(File prev, File curr) {
+        if (prev.isFile() && curr.isFile()) {
+            return Arrays.asList(Pair.of(prev, curr));
+        }
+        
+        Map<String, File> prevJars = getJars(prev);
+        Map<String, File> currJars = getJars(curr);
+        
+        List<Pair<File, File>> retMapping = new ArrayList<>();
+
+        for (String prevKey: (Iterable<String>) prevJars.keySet().stream().sorted(StringUtils::compareIgnoreCase)::iterator) {
+            File prevFile = prevJars.get(prevKey);
+            File curFile = currJars.get(prevKey);
+            if (prevFile != null && curFile != null) {
+                retMapping.add(Pair.of(prevFile, curFile));
+            }
+        }
+        
+        return retMapping;
     }
 
     /**
@@ -88,10 +106,10 @@ static List<String> getCommonJars(File prevDir, File currDir) {
      * @param dir The directory.
      * @return The jar file names.
      */
-    private static Set<String> getJars(File dir) {
-        return Stream.of(dir.listFiles(JAR_FILTER))
-                .map(f -> f.getName())
-                .collect(Collectors.toSet());
+    private static Map<String, File> getJars(File dir) {
+        File[] files = dir.isDirectory() ? dir.listFiles(JAR_FILTER) : new File[]{dir};
+        files = files == null ? new File[0] : files;
+        return Stream.of(files).collect(Collectors.toMap(f -> f.getName(), f -> f, (f1, f2) -> f1));
     }
 
     /**
@@ -106,7 +124,8 @@ private static Set<String> getJars(File dir) {
     private static Set<String> getPublicPackages(File jarFile) throws IOException, IllegalStateException {
         String publicPackageStr = ManifestLoader.loadFromJar(jarFile).getValue("OpenIDE-Module-Public-Packages");
         if (publicPackageStr == null) {
-            throw new IllegalStateException(MessageFormat.format("Manifest for {0} does not have key of 'OpenIDE-Module-Public-Packages'", jarFile.getAbsolutePath()));
+            LOGGER.log(Level.WARNING, MessageFormat.format("Manifest for {0} does not have key of 'OpenIDE-Module-Public-Packages'", jarFile.getAbsolutePath()));
+            return null;
         } else {
             return Stream.of(publicPackageStr.split(","))
                     .map(String::trim)
@@ -139,7 +158,11 @@ static ComparisonRecord getComparison(String prevVersion, String curVersion, Fil
         // scope only to previous or current public packages
         Set<String> prevPublicApiPackages = getPublicPackages(prevJar);
         Set<String> curPublicApiPackages = getPublicPackages(curJar);
-
+        
+        boolean filterToPublicPackages = (prevPublicApiPackages == null && curPublicApiPackages == null) ? false : true;
+        prevPublicApiPackages = prevPublicApiPackages == null ? Collections.emptySet() : prevPublicApiPackages;
+        curPublicApiPackages = curPublicApiPackages == null ? Collections.emptySet() : curPublicApiPackages;
+        
         Set<String> allPublicApiPackages = new HashSet<>();
         allPublicApiPackages.addAll(prevPublicApiPackages);
         allPublicApiPackages.addAll(curPublicApiPackages);
@@ -161,7 +184,10 @@ static ComparisonRecord getComparison(String prevVersion, String curVersion, Fil
 
         JarArchiveComparatorOptions comparatorOptions = new JarArchiveComparatorOptions();
         // only classes in prev or current public api
-        comparatorOptions.getFilters().getExcludes().add((ClassFilter) (CtClass ctClass) -> !allPublicApiPackages.contains(ctClass.getPackageName()));
+        if (filterToPublicPackages) {
+            comparatorOptions.getFilters().getExcludes().add((ClassFilter) (CtClass ctClass) -> !allPublicApiPackages.contains(ctClass.getPackageName()));    
+        }
+        
         // only public classes
         comparatorOptions.getFilters().getExcludes().add((ClassFilter) (CtClass ctClass) -> !Modifier.isPublic(ctClass.getModifiers()));
         // only fields, methods that are public or protected and class is not final
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java
index 8422ed20395ac8cc1bf1a87ec5d7c744481be82d..919897403605c47dcea7cedbada70d58cff00a95 100644
--- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java
@@ -189,13 +189,17 @@ static CLIArgs parseCli(String[] args) throws ParseException {
         File prevVersFile = new File(prevVersPath);
         File srcPathFile = new File(srcPath);
 
-        if (!curVersFile.isDirectory()) {
+        if (!curVersFile.exists()) {
             throw new ParseException("No directory found at " + curVersFile.getAbsolutePath());
         }
 
-        if (!prevVersFile.isDirectory()) {
+        if (!prevVersFile.exists()) {
             throw new ParseException("No directory found at " + prevVersFile.getAbsolutePath());
         }
+        
+        if (curVersFile.isDirectory() != prevVersFile.isDirectory()) {
+            throw new ParseException("Current and previous paths must both be directories or files");
+        }
 
         if (!srcPathFile.isDirectory()) {
             throw new ParseException("No directory found at " + srcPathFile.getAbsolutePath());
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java
index 86b6174b4bcb846c641fcb8a855a1e4d9769424f..78853c4a69c0f23cb6f5efb74cf34cc2846bc427 100644
--- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java
@@ -18,6 +18,7 @@
  */
 package org.sleuthkit.autopsy.apiupdate;
 
+import java.io.File;
 import java.io.IOException;
 import java.text.MessageFormat;
 import java.util.HashMap;
@@ -25,6 +26,7 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.apache.commons.cli.ParseException;
+import org.apache.commons.lang3.tuple.Pair;
 import org.sleuthkit.autopsy.apiupdate.APIDiff.ComparisonRecord;
 import org.sleuthkit.autopsy.apiupdate.CLIProcessor.CLIArgs;
 import org.sleuthkit.autopsy.apiupdate.ModuleUpdates.ModuleVersionNumbers;
@@ -52,19 +54,26 @@ public static void main(String[] args) {
 
         Map<String, ModuleVersionNumbers> newVersionNumMapping = new HashMap<>();
 
-        for (String commonJarFileName : APIDiff.getCommonJars(cliArgs.getPreviousVersPath(), cliArgs.getCurrentVersPath())) {
+        for (Pair<File, File> prevCurJars : APIDiff.getCommonJars(cliArgs.getPreviousVersPath(), cliArgs.getCurrentVersPath())) {
             try {
-                ModuleVersionNumbers prevVersionNums = ModuleUpdates.getVersionsFromJar(cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile());
+                ModuleVersionNumbers prevVersionNums = ModuleUpdates.getVersionsFromJar(prevCurJars.getLeft());
 
                 ComparisonRecord record = APIDiff.getComparison(
                         cliArgs.getPreviousVersion(),
                         cliArgs.getCurrentVersion(),
-                        cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile(),
-                        cliArgs.getCurrentVersPath().toPath().resolve(commonJarFileName).toFile());
+                        prevCurJars.getLeft(),
+                        prevCurJars.getRight());
 
                 ModuleVersionNumbers projectedVersionNums = ModuleUpdates.getModuleVersionUpdate(prevVersionNums, record.getChangeType());
+                
+                String jarFileName;
+                if (prevCurJars.getLeft().getName().equalsIgnoreCase(prevCurJars.getRight().getName())) {
+                    jarFileName = prevCurJars.getLeft().getName();
+                } else {
+                    jarFileName = MessageFormat.format("[previous: {0}, current: {1}]", prevCurJars.getLeft().getName(), prevCurJars.getRight().getName());
+                }
 
-                outputDiff(commonJarFileName, record, prevVersionNums, projectedVersionNums);
+                outputDiff(jarFileName, record, prevVersionNums, projectedVersionNums);
 
                 newVersionNumMapping.put(projectedVersionNums.getRelease().getModuleName(), projectedVersionNums);
             } catch (IOException ex) {
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java
index 8ae574308d8f2184b21619395b96a0fbad1857f2..16e149f854278ff32bc2a25396bd7294c8a0ec8d 100644
--- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java
@@ -110,7 +110,7 @@ public class ModuleUpdates {
      */
     private static SemVer parseSemVer(String semVerStr, SemVer defaultSemVer, String resourceForLogging) {
         if (StringUtils.isBlank(semVerStr)) {
-            LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver for empty string in {0}", resourceForLogging));
+            LOGGER.log(Level.WARNING, MessageFormat.format("Unable to parse semver for empty string in {0}", resourceForLogging));
             return defaultSemVer;
         }
 
@@ -127,7 +127,7 @@ private static SemVer parseSemVer(String semVerStr, SemVer defaultSemVer, String
                 LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver string {0} for {1}", semVerStr, resourceForLogging), ex);
             }
         } else {
-            LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver string {0} for {1}", semVerStr, resourceForLogging));
+            LOGGER.log(Level.WARNING, MessageFormat.format("Unable to parse semver string {0} for {1}", semVerStr, resourceForLogging));
         }
 
         return defaultSemVer;
@@ -154,7 +154,7 @@ private static ReleaseVal parseReleaseVers(String releaseStr, String resourceFor
             }
             return new ReleaseVal(releaseName, releaseNum);
         } else {
-            LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse release version string {0} for {1}", releaseStr, resourceForLogging));
+            LOGGER.log(Level.WARNING, MessageFormat.format("Unable to parse release version string {0} for {1}", releaseStr, resourceForLogging));
         }
 
         return new ReleaseVal("", null);
@@ -175,7 +175,7 @@ private static int tryParse(String str, int defaultVal, String resourceForLoggin
         try {
             return Integer.parseInt(str);
         } catch (NullPointerException | NumberFormatException ex) {
-            LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse version string {0} for {1}", str, resourceForLogging), ex);
+            LOGGER.log(Level.WARNING, MessageFormat.format("Unable to parse version string {0} for {1}", str, resourceForLogging), ex);
             return defaultVal;
         }
     }
@@ -193,8 +193,15 @@ public static ModuleVersionNumbers getVersionsFromJar(File jarFile) throws IOExc
         SemVer specSemVer = parseSemVer(spec, DEFAULT_SEMVER,
                 MessageFormat.format("{0} in manifest for {1}", MANIFEST_SPEC_KEY, jarFile));
 
-        int implementation = tryParse(manifest.getValue(MANIFEST_IMPL_KEY), DEFAULT_VERS_VAL,
-                MessageFormat.format("{0} in manifest for {1}", MANIFEST_IMPL_KEY, jarFile));
+        String implStr = manifest.getValue(MANIFEST_IMPL_KEY);
+        int implementation;
+        if (StringUtils.isBlank(implStr)) {
+            LOGGER.log(Level.WARNING, MessageFormat.format("No {0} for implementation found in {1}.  Using default of {2}.", MANIFEST_IMPL_KEY, jarFile.getName(), DEFAULT_VERS_VAL));
+            implementation = DEFAULT_VERS_VAL;
+        } else {
+            implementation = tryParse(implStr, DEFAULT_VERS_VAL,
+                    MessageFormat.format("{0} in manifest for {1}", MANIFEST_IMPL_KEY, jarFile));
+        }
 
         ReleaseVal release = parseReleaseVers(manifest.getValue(MANIFEST_RELEASE_KEY),
                 MessageFormat.format("{0} in manifest for {1}", MANIFEST_RELEASE_KEY, jarFile));