Merge branch 'stable-2.16' into stable-3.0

* stable-2.16:
  ErrorProne: Enable and fix UnusedException check
  Update git submodules

Also fix a few more instances of UnusedException.

Change-Id: Ia9864e78004f17508e13f4bf49d895d470da4942
diff --git a/java/com/google/gerrit/acceptance/InProcessProtocol.java b/java/com/google/gerrit/acceptance/InProcessProtocol.java
index 30845a8..69715a9 100644
--- a/java/com/google/gerrit/acceptance/InProcessProtocol.java
+++ b/java/com/google/gerrit/acceptance/InProcessProtocol.java
@@ -235,7 +235,7 @@
       try {
         perm.check(ProjectPermission.RUN_UPLOAD_PACK);
       } catch (AuthException e) {
-        throw new ServiceNotAuthorizedException();
+        throw new ServiceNotAuthorizedException(e.getMessage(), e);
       } catch (PermissionBackendException e) {
         throw new RuntimeException(e);
       }
@@ -308,7 +308,7 @@
             .project(req.project)
             .check(ProjectPermission.RUN_RECEIVE_PACK);
       } catch (AuthException e) {
-        throw new ServiceNotAuthorizedException();
+        throw new ServiceNotAuthorizedException(e.getMessage(), e);
       } catch (PermissionBackendException e) {
         throw new RuntimeException(e);
       }
diff --git a/java/com/google/gerrit/exceptions/InvalidSshKeyException.java b/java/com/google/gerrit/exceptions/InvalidSshKeyException.java
index 8baba20..dda80cf 100644
--- a/java/com/google/gerrit/exceptions/InvalidSshKeyException.java
+++ b/java/com/google/gerrit/exceptions/InvalidSshKeyException.java
@@ -23,4 +23,8 @@
   public InvalidSshKeyException() {
     super(MESSAGE);
   }
+
+  public InvalidSshKeyException(Throwable cause) {
+    super(MESSAGE, cause);
+  }
 }
diff --git a/java/com/google/gerrit/extensions/restapi/MethodNotAllowedException.java b/java/com/google/gerrit/extensions/restapi/MethodNotAllowedException.java
index 61c6345..235b77f 100644
--- a/java/com/google/gerrit/extensions/restapi/MethodNotAllowedException.java
+++ b/java/com/google/gerrit/extensions/restapi/MethodNotAllowedException.java
@@ -26,4 +26,12 @@
   public MethodNotAllowedException(String msg) {
     super(msg);
   }
+
+  /**
+   * @param msg error text for client describing why the method is not allowed.
+   * @param cause reason for the method not being allowed.
+   */
+  public MethodNotAllowedException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
 }
diff --git a/java/com/google/gerrit/extensions/restapi/NotImplementedException.java b/java/com/google/gerrit/extensions/restapi/NotImplementedException.java
index 566159d..d74986a7 100644
--- a/java/com/google/gerrit/extensions/restapi/NotImplementedException.java
+++ b/java/com/google/gerrit/extensions/restapi/NotImplementedException.java
@@ -25,4 +25,8 @@
   public NotImplementedException(String message) {
     super(message);
   }
+
+  public NotImplementedException(String message, Throwable cause) {
+    super(message, cause);
+  }
 }
diff --git a/java/com/google/gerrit/extensions/restapi/ResourceNotFoundException.java b/java/com/google/gerrit/extensions/restapi/ResourceNotFoundException.java
index e676828..fa2288a 100644
--- a/java/com/google/gerrit/extensions/restapi/ResourceNotFoundException.java
+++ b/java/com/google/gerrit/extensions/restapi/ResourceNotFoundException.java
@@ -34,6 +34,10 @@
     super("Not found: " + id.get());
   }
 
+  public ResourceNotFoundException(IdString id, Throwable cause) {
+    super("Not found: " + id.get(), cause);
+  }
+
   @SuppressWarnings("unchecked")
   @Override
   public ResourceNotFoundException caching(CacheControl c) {
diff --git a/java/com/google/gerrit/httpd/HtmlDomUtil.java b/java/com/google/gerrit/httpd/HtmlDomUtil.java
index 25ae71c..57f2664 100644
--- a/java/com/google/gerrit/httpd/HtmlDomUtil.java
+++ b/java/com/google/gerrit/httpd/HtmlDomUtil.java
@@ -131,7 +131,7 @@
     try {
       d = newBuilder().newDocument();
     } catch (ParserConfigurationException e) {
-      throw new IOException("Cannot clone document");
+      throw new IOException("Cannot clone document", e);
     }
     Node n = d.importNode(doc.getDocumentElement(), true);
     d.appendChild(n);
diff --git a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
index 30ebe6e..4461a52 100644
--- a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
@@ -195,7 +195,7 @@
       defaultAuthPlugin = loginProvider.getPluginName();
       defaultAuthProvider = loginProvider.getExportName();
     } catch (NoSuchElementException e) {
-      throw new ServletException("No OAuth login provider installed");
+      throw new ServletException("No OAuth login provider installed", e);
     } catch (IllegalArgumentException e) {
       // multiple providers found => do not pick any
     }
diff --git a/java/com/google/gerrit/httpd/plugins/PluginServletContext.java b/java/com/google/gerrit/httpd/plugins/PluginServletContext.java
index 6a8ef32..40083e4 100644
--- a/java/com/google/gerrit/httpd/plugins/PluginServletContext.java
+++ b/java/com/google/gerrit/httpd/plugins/PluginServletContext.java
@@ -61,9 +61,11 @@
       try {
         handler = API.class.getDeclaredMethod(method.getName(), method.getParameterTypes());
       } catch (NoSuchMethodException e) {
-        throw new NoSuchMethodError(
+        String msg =
             String.format(
-                "%s does not implement %s", PluginServletContext.class, method.toGenericString()));
+                "%s does not implement %s", PluginServletContext.class, method.toGenericString());
+        logger.atSevere().withCause(e).log(msg);
+        throw new NoSuchMethodError(msg);
       }
       return handler.invoke(this, args);
     }
diff --git a/java/com/google/gerrit/httpd/raw/BazelBuild.java b/java/com/google/gerrit/httpd/raw/BazelBuild.java
index a13078d..7677e97 100644
--- a/java/com/google/gerrit/httpd/raw/BazelBuild.java
+++ b/java/com/google/gerrit/httpd/raw/BazelBuild.java
@@ -63,8 +63,9 @@
     try {
       status = rebuild.waitFor();
     } catch (InterruptedException e) {
-      throw new InterruptedIOException(
-          "interrupted waiting for: " + Joiner.on(' ').join(proc.command()));
+      String msg = "interrupted waiting for: " + Joiner.on(' ').join(proc.command());
+      logger.atSevere().withCause(e).log(msg);
+      throw new InterruptedIOException(msg);
     }
     if (status != 0) {
       logger.atWarning().log("build failed: %s", new String(out, UTF_8));
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 45859d4..4726df3 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -889,7 +889,7 @@
           try {
             first = json.peek();
           } catch (EOFException e) {
-            throw new BadRequestException("Expected JSON object");
+            throw new BadRequestException("Expected JSON object", e);
           }
           if (first == JsonToken.STRING) {
             return parseString(json.nextString(), type);
diff --git a/java/com/google/gerrit/lucene/QueryBuilder.java b/java/com/google/gerrit/lucene/QueryBuilder.java
index ce5ba98..8dfc12e 100644
--- a/java/com/google/gerrit/lucene/QueryBuilder.java
+++ b/java/com/google/gerrit/lucene/QueryBuilder.java
@@ -167,7 +167,7 @@
       // subclasses of OperatorPredicate.
       value = Integer.parseInt(p.getValue());
     } catch (NumberFormatException e) {
-      throw new QueryParseException("not an integer: " + p.getValue());
+      throw new QueryParseException("not an integer: " + p.getValue(), e);
     }
     return new TermQuery(intTerm(p.getField().getName(), value));
   }
diff --git a/java/com/google/gerrit/pgm/init/BaseInit.java b/java/com/google/gerrit/pgm/init/BaseInit.java
index b96c6f7..0bc1860 100644
--- a/java/com/google/gerrit/pgm/init/BaseInit.java
+++ b/java/com/google/gerrit/pgm/init/BaseInit.java
@@ -331,7 +331,7 @@
       IoUtil.loadJARs(secureStoreLib);
       return new SecureStoreInitData(secureStoreLib, secureStores.get(0));
     } catch (IOException e) {
-      throw new InvalidSecureStoreException(String.format("%s is not a valid jar", secureStore));
+      throw new InvalidSecureStoreException(String.format("%s is not a valid jar", secureStore), e);
     }
   }
 
diff --git a/java/com/google/gerrit/pgm/init/InitHttpd.java b/java/com/google/gerrit/pgm/init/InitHttpd.java
index 027a3ac..b9fe874 100644
--- a/java/com/google/gerrit/pgm/init/InitHttpd.java
+++ b/java/com/google/gerrit/pgm/init/InitHttpd.java
@@ -169,7 +169,7 @@
         uri = new URI(s + "://" + uri.getHost() + uri.getPath());
       }
     } catch (URISyntaxException e) {
-      throw die("invalid httpd.listenUrl");
+      throw die("invalid httpd.listenUrl", e);
     }
     httpd.set("listenUrl", urlbuf.toString());
     gerrit.string("Canonical URL", "canonicalWebUrl", uri.toString());
diff --git a/java/com/google/gerrit/server/ApprovalsUtil.java b/java/com/google/gerrit/server/ApprovalsUtil.java
index 865e33c..135276e 100644
--- a/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -318,7 +318,7 @@
         forChange.check(new LabelPermission.WithValue(name, value));
       } catch (AuthException e) {
         throw new AuthException(
-            String.format("applying label \"%s\": %d is restricted", name, value));
+            String.format("applying label \"%s\": %d is restricted", name, value), e);
       }
     }
   }
diff --git a/java/com/google/gerrit/server/account/AccountManager.java b/java/com/google/gerrit/server/account/AccountManager.java
index f62ad26..82acbfd 100644
--- a/java/com/google/gerrit/server/account/AccountManager.java
+++ b/java/com/google/gerrit/server/account/AccountManager.java
@@ -390,7 +390,7 @@
     try {
       groupsUpdate.updateGroup(groupUuid, groupUpdate);
     } catch (NoSuchGroupException e) {
-      throw new AccountException(String.format("Group %s not found", groupUuid));
+      throw new AccountException(String.format("Group %s not found", groupUuid), e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalId.java b/java/com/google/gerrit/server/account/externalids/ExternalId.java
index c363b5b..9baa7dd 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalId.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalId.java
@@ -24,6 +24,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.common.flogger.FluentLogger;
 import com.google.common.hash.Hashing;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.client.AuthType;
@@ -43,6 +44,8 @@
 
 @AutoValue
 public abstract class ExternalId implements Serializable {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
   // If these regular expressions are modified the same modifications should be done to the
   // corresponding regular expressions in the
   // com.google.gerrit.client.account.UsernameField class.
@@ -391,11 +394,12 @@
       }
       return accountId;
     } catch (IllegalArgumentException e) {
-      throw invalidConfig(
-          noteId,
+      String msg =
           String.format(
               "Value %s for '%s.%s.%s' is invalid, expected account ID",
-              accountIdStr, EXTERNAL_ID_SECTION, externalIdKeyStr, ACCOUNT_ID_KEY));
+              accountIdStr, EXTERNAL_ID_SECTION, externalIdKeyStr, ACCOUNT_ID_KEY);
+      logger.atSevere().withCause(e).log(msg);
+      throw invalidConfig(noteId, msg);
     }
   }
 
diff --git a/java/com/google/gerrit/server/args4j/AccountIdHandler.java b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
index efc1866..37e52f4 100644
--- a/java/com/google/gerrit/server/args4j/AccountIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
@@ -16,6 +16,7 @@
 
 import static com.google.gerrit.util.cli.Localizable.localizable;
 
+import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -38,6 +39,8 @@
 import org.kohsuke.args4j.spi.Setter;
 
 public class AccountIdHandler extends OptionHandler<Account.Id> {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
   private final AccountResolver accountResolver;
   private final AccountManager accountManager;
   private final AuthType authType;
@@ -78,11 +81,15 @@
           case OPENID:
           case OPENID_SSO:
           default:
-            throw new CmdLineException(owner, localizable("user \"%s\" not found"), token);
+            String msg = "user \"%s\" not found";
+            logger.atSevere().withCause(e).log(msg, token);
+            throw new CmdLineException(owner, localizable(msg), token);
         }
       }
     } catch (StorageException e) {
-      throw new CmdLineException(owner, localizable("database is down"));
+      String msg = "database is down";
+      logger.atSevere().withCause(e).log(msg);
+      throw new CmdLineException(owner, localizable(msg));
     } catch (IOException e) {
       throw new CmdLineException(owner, "Failed to load account", e);
     } catch (ConfigInvalidException e) {
@@ -102,7 +109,9 @@
       req.setSkipAuthentication(true);
       return accountManager.authenticate(req).getAccountId();
     } catch (AccountException e) {
-      throw new CmdLineException(owner, localizable("user \"%s\" not found"), user);
+      String msg = "user \"%s\" not found";
+      logger.atSevere().withCause(e).log(msg, user);
+      throw new CmdLineException(owner, localizable(msg), user);
     }
   }
 
diff --git a/java/com/google/gerrit/server/args4j/ChangeIdHandler.java b/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
index 77f0fd8..620bfa1 100644
--- a/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
@@ -66,7 +66,7 @@
         return 1;
       }
     } catch (IllegalArgumentException e) {
-      throw new CmdLineException(owner, localizable("Change-Id is not valid"));
+      throw new CmdLineException(owner, localizable("Change-Id is not valid: %s"), e.getMessage());
     } catch (StorageException e) {
       throw new CmdLineException(owner, localizable("Database error: %s"), e.getMessage());
     }
diff --git a/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java b/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java
index 84c1d88..4581fe0 100644
--- a/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java
@@ -43,7 +43,8 @@
     try {
       id = PatchSet.Id.parse(token);
     } catch (IllegalArgumentException e) {
-      throw new CmdLineException(owner, localizable("\"%s\" is not a valid patch set"), token);
+      throw new CmdLineException(
+          owner, localizable("\"%s\" is not a valid patch set: %s"), token, e.getMessage());
     }
 
     setter.addValue(id);
diff --git a/java/com/google/gerrit/server/args4j/ProjectHandler.java b/java/com/google/gerrit/server/args4j/ProjectHandler.java
index f33a4ed..e3af82b 100644
--- a/java/com/google/gerrit/server/args4j/ProjectHandler.java
+++ b/java/com/google/gerrit/server/args4j/ProjectHandler.java
@@ -89,7 +89,7 @@
       permissionBackend.currentUser().project(nameKey).check(permissionToCheck);
     } catch (AuthException e) {
       throw new CmdLineException(
-          owner, localizable(new NoSuchProjectException(nameKey).getMessage()));
+          owner, localizable(new NoSuchProjectException(nameKey, e).getMessage()));
     } catch (PermissionBackendException | IOException e) {
       logger.atWarning().withCause(e).log("Cannot load project %s", nameWithoutSuffix);
       throw new CmdLineException(
diff --git a/java/com/google/gerrit/server/cache/serialize/IntegerCacheSerializer.java b/java/com/google/gerrit/server/cache/serialize/IntegerCacheSerializer.java
index 4494454..aca9b05 100644
--- a/java/com/google/gerrit/server/cache/serialize/IntegerCacheSerializer.java
+++ b/java/com/google/gerrit/server/cache/serialize/IntegerCacheSerializer.java
@@ -37,7 +37,7 @@
       cout.writeInt32NoTag(requireNonNull(object));
       cout.flush();
     } catch (IOException e) {
-      throw new IllegalStateException("Failed to serialize int");
+      throw new IllegalStateException("Failed to serialize int", e);
     }
     int n = cout.getTotalBytesWritten();
     return n == buf.length ? buf : Arrays.copyOfRange(buf, 0, n);
@@ -50,7 +50,7 @@
     try {
       ret = cin.readRawVarint32();
     } catch (IOException e) {
-      throw new IllegalArgumentException("Failed to deserialize int");
+      throw new IllegalArgumentException("Failed to deserialize int", e);
     }
     int n = cin.getTotalBytesRead();
     if (n != in.length) {
diff --git a/java/com/google/gerrit/server/change/PureRevert.java b/java/com/google/gerrit/server/change/PureRevert.java
index 0859634..acb3dd7 100644
--- a/java/com/google/gerrit/server/change/PureRevert.java
+++ b/java/com/google/gerrit/server/change/PureRevert.java
@@ -50,7 +50,7 @@
     try {
       claimedOriginalObjectId = ObjectId.fromString(claimedOriginal.get());
     } catch (InvalidObjectIdException e) {
-      throw new BadRequestException("invalid object ID");
+      throw new BadRequestException("invalid object ID", e);
     }
 
     return pureRevertCache.isPureRevert(
diff --git a/java/com/google/gerrit/server/config/ConfigUtil.java b/java/com/google/gerrit/server/config/ConfigUtil.java
index f476adf..43c05e0 100644
--- a/java/com/google/gerrit/server/config/ConfigUtil.java
+++ b/java/com/google/gerrit/server/config/ConfigUtil.java
@@ -30,7 +30,6 @@
 import org.eclipse.jgit.lib.Config;
 
 public class ConfigUtil {
-
   @SuppressWarnings("unchecked")
   private static <T> T[] allValuesOf(T defaultValue) {
     try {
@@ -182,7 +181,7 @@
     try {
       return getTimeUnit(s, defaultValue, wantUnit);
     } catch (IllegalArgumentException notTime) {
-      throw notTimeUnit(section, subsection, setting, valueString);
+      throw notTimeUnit(section, subsection, setting, valueString, notTime);
     }
   }
 
@@ -250,7 +249,7 @@
     try {
       return wantUnit.convert(Long.parseLong(digits) * inputMul, inputUnit);
     } catch (NumberFormatException nfe) {
-      throw notTimeUnit(valueString);
+      throw notTimeUnit(valueString, nfe);
     }
   }
 
@@ -421,13 +420,21 @@
   }
 
   private static IllegalArgumentException notTimeUnit(
-      final String section,
-      final String subsection,
-      final String setting,
-      final String valueString) {
-    return new IllegalArgumentException(
-        "Invalid time unit value: "
-            + section
+      String section, String subsection, String setting, String valueString, Throwable why) {
+    return notTimeUnit(
+        section
+            + (subsection != null ? "." + subsection : "")
+            + "."
+            + setting
+            + " = "
+            + valueString,
+        why);
+  }
+
+  private static IllegalArgumentException notTimeUnit(
+      String section, String subsection, String setting, String valueString) {
+    return notTimeUnit(
+        section
             + (subsection != null ? "." + subsection : "")
             + "."
             + setting
@@ -439,5 +446,9 @@
     return new IllegalArgumentException("Invalid time unit value: " + val);
   }
 
+  private static IllegalArgumentException notTimeUnit(String val, Throwable why) {
+    return new IllegalArgumentException("Invalid time unit value: " + val, why);
+  }
+
   private ConfigUtil() {}
 }
diff --git a/java/com/google/gerrit/server/git/MergeUtil.java b/java/com/google/gerrit/server/git/MergeUtil.java
index 5e2ad47..c1d8ddd 100644
--- a/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/java/com/google/gerrit/server/git/MergeUtil.java
@@ -748,6 +748,7 @@
       try {
         failed(rw, mergeTip, n, getCommitMergeStatus(e.getReason()));
       } catch (IOException e2) {
+        logger.atSevere().withCause(e2).log("Failed to set merge failure status for " + n.name());
         throw new IntegrationException("Cannot merge " + n.name(), e);
       }
     } catch (IOException e) {
diff --git a/java/com/google/gerrit/server/git/PureRevertCache.java b/java/com/google/gerrit/server/git/PureRevertCache.java
index 1daa1d5..16ac87f 100644
--- a/java/com/google/gerrit/server/git/PureRevertCache.java
+++ b/java/com/google/gerrit/server/git/PureRevertCache.java
@@ -163,7 +163,7 @@
           try {
             claimedOriginalCommit = rw.parseCommit(original);
           } catch (InvalidObjectIdException | MissingObjectException e) {
-            throw new BadRequestException("invalid object ID");
+            throw new BadRequestException("invalid object ID", e);
           }
           if (claimedOriginalCommit.getParentCount() == 0) {
             throw new BadRequestException("can't check against initial commit");
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidationException.java b/java/com/google/gerrit/server/git/validators/CommitValidationException.java
index bffe382..aeead63 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidationException.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidationException.java
@@ -27,6 +27,11 @@
     this.messages = ImmutableList.of(message);
   }
 
+  public CommitValidationException(String reason, CommitValidationMessage message, Throwable why) {
+    super(reason, why);
+    this.messages = ImmutableList.of(message);
+  }
+
   public CommitValidationException(String reason, List<CommitValidationMessage> messages) {
     super(reason);
     this.messages = ImmutableList.copyOf(messages);
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index a3c6138..3c36273 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -467,7 +467,7 @@
         perm.check(RefPermission.MERGE);
         return Collections.emptyList();
       } catch (AuthException e) {
-        throw new CommitValidationException("you are not allowed to upload merges");
+        throw new CommitValidationException("you are not allowed to upload merges", e);
       } catch (PermissionBackendException e) {
         logger.atSevere().withCause(e).log("cannot check MERGE");
         throw new CommitValidationException("internal auth error");
@@ -564,7 +564,7 @@
           perm.check(RefPermission.FORGE_COMMITTER);
         } catch (AuthException denied) {
           throw new CommitValidationException(
-              "not Signed-off-by author/committer/uploader in message footer");
+              "not Signed-off-by author/committer/uploader in message footer", denied);
         } catch (PermissionBackendException e) {
           logger.atSevere().withCause(e).log("cannot check FORGE_COMMITTER");
           throw new CommitValidationException("internal auth error");
@@ -599,7 +599,7 @@
         return Collections.emptyList();
       } catch (AuthException e) {
         throw new CommitValidationException(
-            "invalid author", invalidEmail("author", author, user, urlFormatter));
+            "invalid author", invalidEmail("author", author, user, urlFormatter), e);
       } catch (PermissionBackendException e) {
         logger.atSevere().withCause(e).log("cannot check FORGE_AUTHOR");
         throw new CommitValidationException("internal auth error");
@@ -632,7 +632,7 @@
         return Collections.emptyList();
       } catch (AuthException e) {
         throw new CommitValidationException(
-            "invalid committer", invalidEmail("committer", committer, user, urlFormatter));
+            "invalid committer", invalidEmail("committer", committer, user, urlFormatter), e);
       } catch (PermissionBackendException e) {
         logger.atSevere().withCause(e).log("cannot check FORGE_COMMITTER");
         throw new CommitValidationException("internal auth error");
@@ -671,7 +671,8 @@
                   "pushing merge commit %s by %s requires '%s' permission",
                   receiveEvent.commit.getId(),
                   gerritIdent.getEmailAddress(),
-                  RefPermission.FORGE_SERVER.name()));
+                  RefPermission.FORGE_SERVER.name()),
+              denied);
         } catch (PermissionBackendException e) {
           logger.atSevere().withCause(e).log("cannot check FORGE_SERVER");
           throw new CommitValidationException("internal auth error");
diff --git a/java/com/google/gerrit/server/git/validators/MergeValidationException.java b/java/com/google/gerrit/server/git/validators/MergeValidationException.java
index 3624fe0..2b7803d 100644
--- a/java/com/google/gerrit/server/git/validators/MergeValidationException.java
+++ b/java/com/google/gerrit/server/git/validators/MergeValidationException.java
@@ -28,4 +28,8 @@
   public MergeValidationException(String msg) {
     super(msg);
   }
+
+  public MergeValidationException(String msg, Throwable why) {
+    super(msg, why);
+  }
 }
diff --git a/java/com/google/gerrit/server/git/validators/MergeValidators.java b/java/com/google/gerrit/server/git/validators/MergeValidators.java
index e7e021b..75be8f3 100644
--- a/java/com/google/gerrit/server/git/validators/MergeValidators.java
+++ b/java/com/google/gerrit/server/git/validators/MergeValidators.java
@@ -178,7 +178,7 @@
                 try {
                   permissionBackend.user(caller).check(GlobalPermission.ADMINISTRATE_SERVER);
                 } catch (AuthException e) {
-                  throw new MergeValidationException(SET_BY_ADMIN);
+                  throw new MergeValidationException(SET_BY_ADMIN, e);
                 } catch (PermissionBackendException e) {
                   logger.atWarning().withCause(e).log("Cannot check ADMINISTRATE_SERVER");
                   throw new MergeValidationException("validation unavailable");
@@ -190,7 +190,7 @@
                       .project(destProject.getNameKey())
                       .check(ProjectPermission.WRITE_CONFIG);
                 } catch (AuthException e) {
-                  throw new MergeValidationException(SET_BY_OWNER);
+                  throw new MergeValidationException(SET_BY_OWNER, e);
                 } catch (PermissionBackendException e) {
                   logger.atWarning().withCause(e).log("Cannot check WRITE_CONFIG");
                   throw new MergeValidationException("validation unavailable");
@@ -231,7 +231,7 @@
             }
           }
         } catch (ConfigInvalidException | IOException e) {
-          throw new MergeValidationException(INVALID_CONFIG);
+          throw new MergeValidationException(INVALID_CONFIG, e);
         }
       }
     }
diff --git a/java/com/google/gerrit/server/git/validators/RefOperationValidators.java b/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
index dd5d508..3c0208c 100644
--- a/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
+++ b/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
@@ -132,7 +132,7 @@
             try {
               perm.check(GlobalPermission.ACCESS_DATABASE);
             } catch (AuthException | PermissionBackendException e) {
-              throw new ValidationException("Not allowed to create user branch.");
+              throw new ValidationException("Not allowed to create user branch.", e);
             }
             if (Account.Id.fromRef(refEvent.command.getRefName()) == null) {
               throw new ValidationException(
@@ -143,7 +143,7 @@
             try {
               perm.check(GlobalPermission.ACCESS_DATABASE);
             } catch (AuthException | PermissionBackendException e) {
-              throw new ValidationException("Not allowed to delete user branch.");
+              throw new ValidationException("Not allowed to delete user branch.", e);
             }
           }
         }
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index 90c1a80..4888e65 100644
--- a/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -296,7 +296,7 @@
       try {
         ps = args.patchSetUtil.get(changeData.notes(), new PatchSet.Id(change.getId(), patchSetId));
       } catch (StorageException e) {
-        throw new PatchListNotAvailableException("Failed to get patchSet");
+        throw new PatchListNotAvailableException("Failed to get patchSet", e);
       }
     }
     return args.patchListCache.get(change, ps);
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 7ba5679..d3b34b9 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -912,7 +912,9 @@
     try {
       adr = Address.parse(line);
     } catch (IllegalArgumentException e) {
-      throw invalidFooter(state.getByEmailFooterKey(), line);
+      ConfigInvalidException cie = invalidFooter(state.getByEmailFooterKey(), line);
+      cie.initCause(e);
+      throw cie;
     }
     if (!reviewersByEmail.containsRow(adr)) {
       reviewersByEmail.put(adr, state, ts);
diff --git a/java/com/google/gerrit/server/patch/PatchScriptFactory.java b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
index 08ee7a3..a7e7738 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptFactory.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
@@ -199,7 +199,7 @@
       try {
         permissionBackend.currentUser().change(notes).check(ChangePermission.READ);
       } catch (AuthException e) {
-        throw new NoSuchChangeException(changeId);
+        throw new NoSuchChangeException(changeId, e);
       }
     }
 
diff --git a/java/com/google/gerrit/server/project/ProjectCreator.java b/java/com/google/gerrit/server/project/ProjectCreator.java
index b50b046..35ecd7c 100644
--- a/java/com/google/gerrit/server/project/ProjectCreator.java
+++ b/java/com/google/gerrit/server/project/ProjectCreator.java
@@ -125,9 +125,10 @@
               + nameKey.get()
               + " because the name is already occupied by another project."
               + " The other project has the same name, only spelled in a"
-              + " different case.");
+              + " different case.",
+          e);
     } catch (RepositoryNotFoundException badName) {
-      throw new BadRequestException("invalid project name: " + nameKey);
+      throw new BadRequestException("invalid project name: " + nameKey, badName);
     } catch (ConfigInvalidException e) {
       String msg = "Cannot create " + nameKey;
       logger.atSevere().withCause(e).log(msg);
diff --git a/java/com/google/gerrit/server/project/RefUtil.java b/java/com/google/gerrit/server/project/RefUtil.java
index 4e08137..a9c964d 100644
--- a/java/com/google/gerrit/server/project/RefUtil.java
+++ b/java/com/google/gerrit/server/project/RefUtil.java
@@ -55,7 +55,7 @@
           "Cannot resolve \"%s\" in project \"%s\"", baseRevision, projectName.get());
       throw new InvalidRevisionException(baseRevision);
     } catch (RevisionSyntaxException err) {
-      throw new InvalidRevisionException(baseRevision);
+      throw new InvalidRevisionException(baseRevision, err);
     }
   }
 
@@ -66,7 +66,7 @@
       try {
         rw.markStart(rw.parseCommit(revid));
       } catch (IncorrectObjectTypeException err) {
-        throw new InvalidRevisionException(revid.name());
+        throw new InvalidRevisionException(revid.name(), err);
       }
       RefDatabase refDb = repo.getRefDatabase();
       Iterable<Ref> refs =
@@ -86,7 +86,7 @@
       rw.checkConnectivity();
       return rw;
     } catch (IncorrectObjectTypeException | MissingObjectException err) {
-      throw new InvalidRevisionException(revid.name());
+      throw new InvalidRevisionException(revid.name(), err);
     } catch (IOException err) {
       logger.atSevere().withCause(err).log(
           "Repository \"%s\" may be corrupt; suggest running git fsck", repo.getDirectory());
@@ -128,5 +128,9 @@
     InvalidRevisionException(@Nullable String invalidRevision) {
       super(MESSAGE + ": " + invalidRevision);
     }
+
+    InvalidRevisionException(@Nullable String invalidRevision, Throwable why) {
+      super(MESSAGE + ": " + invalidRevision, why);
+    }
   }
 }
diff --git a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
index 70f4a2d..38336c1 100644
--- a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
@@ -123,7 +123,9 @@
     try {
       args.permissionBackend.user(args.getUser()).change(changeNotes).check(ChangePermission.READ);
     } catch (AuthException e) {
-      throw error(String.format("change %s not found", change));
+      String msg = String.format("change %s not found", change);
+      logger.atSevere().withCause(e).log(msg);
+      throw error(msg);
     }
 
     return AccountPredicates.cansee(args, changeNotes);
diff --git a/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java b/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
index 570da6b..8b4cb53 100644
--- a/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
@@ -75,7 +75,7 @@
     try {
       parsedState = ProjectState.valueOf(state.replace('-', '_').toUpperCase());
     } catch (IllegalArgumentException e) {
-      throw error("state operator must be either 'active' or 'read-only'");
+      throw error("state operator must be either 'active' or 'read-only'", e);
     }
     if (parsedState == ProjectState.HIDDEN) {
       throw error("state operator must be either 'active' or 'read-only'");
diff --git a/java/com/google/gerrit/server/restapi/account/Capabilities.java b/java/com/google/gerrit/server/restapi/account/Capabilities.java
index 07b1214..3d719ff9 100644
--- a/java/com/google/gerrit/server/restapi/account/Capabilities.java
+++ b/java/com/google/gerrit/server/restapi/account/Capabilities.java
@@ -75,7 +75,7 @@
       permissionBackend.absentUser(target.getAccountId()).check(perm);
       return new AccountResource.Capability(target, globalOrPluginPermissionName(perm));
     } catch (AuthException e) {
-      throw new ResourceNotFoundException(id);
+      throw new ResourceNotFoundException(id, e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/account/CreateAccount.java b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
index b19994c..bf50e10 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
@@ -156,7 +156,7 @@
       try {
         addGroupMember(groupUuid, accountId);
       } catch (NoSuchGroupException e) {
-        throw new UnprocessableEntityException(String.format("Group %s not found", groupUuid));
+        throw new UnprocessableEntityException(String.format("Group %s not found", groupUuid), e);
       }
     }
 
diff --git a/java/com/google/gerrit/server/restapi/account/PutAgreement.java b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
index 7bac359..147eec8 100644
--- a/java/com/google/gerrit/server/restapi/account/PutAgreement.java
+++ b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
@@ -95,7 +95,7 @@
     try {
       addMembers.addMembers(uuid, ImmutableSet.of(accountState.getAccount().getId()));
     } catch (NoSuchGroupException e) {
-      throw new ResourceConflictException("autoverify group not found");
+      throw new ResourceConflictException("autoverify group not found", e);
     }
     agreementSignup.fire(accountState, agreementName);
 
diff --git a/java/com/google/gerrit/server/restapi/account/PutUsername.java b/java/com/google/gerrit/server/restapi/account/PutUsername.java
index bc1ffc8..76aece7 100644
--- a/java/com/google/gerrit/server/restapi/account/PutUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/PutUsername.java
@@ -115,7 +115,7 @@
       }
 
       // Otherwise, someone else has this identity.
-      throw new ResourceConflictException("username already used");
+      throw new ResourceConflictException("username already used", dupeErr);
     }
 
     sshKeyCache.evict(input.username);
diff --git a/java/com/google/gerrit/server/restapi/account/SshKeys.java b/java/com/google/gerrit/server/restapi/account/SshKeys.java
index 6e3f905..a6c4d80 100644
--- a/java/com/google/gerrit/server/restapi/account/SshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/SshKeys.java
@@ -70,7 +70,7 @@
         permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
       } catch (AuthException e) {
         // If lacking MODIFY_ACCOUNT claim the resource does not exist.
-        throw new ResourceNotFoundException();
+        throw new ResourceNotFoundException(id, e);
       }
     }
     return parse(rsrc.getUser(), id);
@@ -86,7 +86,7 @@
       }
       return new AccountResource.SshKey(user, sshKey);
     } catch (NumberFormatException e) {
-      throw new ResourceNotFoundException(id);
+      throw new ResourceNotFoundException(id, e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/account/StarredChanges.java b/java/com/google/gerrit/server/restapi/account/StarredChanges.java
index 3c14ad3..c108dcb 100644
--- a/java/com/google/gerrit/server/restapi/account/StarredChanges.java
+++ b/java/com/google/gerrit/server/restapi/account/StarredChanges.java
@@ -123,10 +123,10 @@
       try {
         change = changes.parse(TopLevelResource.INSTANCE, id);
       } catch (ResourceNotFoundException e) {
-        throw new UnprocessableEntityException(String.format("change %s not found", id.get()));
+        throw new UnprocessableEntityException(String.format("change %s not found", id.get()), e);
       } catch (StorageException | PermissionBackendException | IOException e) {
         logger.atSevere().withCause(e).log("cannot resolve change");
-        throw new UnprocessableEntityException("internal server error");
+        throw new UnprocessableEntityException("internal server error", e);
       }
 
       try {
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 9b45ba2..c1fd0ba 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -286,7 +286,8 @@
     try {
       baseObjectId = ObjectId.fromString(base);
     } catch (InvalidObjectIdException e) {
-      throw new BadRequestException(String.format("Base %s doesn't represent a valid SHA-1", base));
+      throw new BadRequestException(
+          String.format("Base %s doesn't represent a valid SHA-1", base), e);
     }
 
     RevCommit baseCommit;
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index b6a242c..0d1a001 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -254,7 +254,7 @@
     try {
       permissionBackend.currentUser().project(project).ref(refName).check(RefPermission.READ);
     } catch (AuthException e) {
-      throw new ResourceNotFoundException(String.format("ref %s not found", refName));
+      throw new ResourceNotFoundException(String.format("ref %s not found", refName), e);
     }
 
     permissionBackend
@@ -332,7 +332,7 @@
     try {
       permissionBackend.currentUser().change(change).check(ChangePermission.READ);
     } catch (AuthException e) {
-      throw new UnprocessableEntityException("Read not permitted for " + baseChange);
+      throw new UnprocessableEntityException("Read not permitted for " + baseChange, e);
     }
 
     return change;
@@ -360,14 +360,15 @@
         parentCommit = ObjectId.fromString(baseCommit);
       } catch (InvalidObjectIdException e) {
         throw new UnprocessableEntityException(
-            String.format("Base %s doesn't represent a valid SHA-1", baseCommit));
+            String.format("Base %s doesn't represent a valid SHA-1", baseCommit), e);
       }
 
       RevCommit parentRevCommit;
       try {
         parentRevCommit = revWalk.parseCommit(parentCommit);
       } catch (MissingObjectException e) {
-        throw new UnprocessableEntityException(String.format("Base %s doesn't exist", baseCommit));
+        throw new UnprocessableEntityException(
+            String.format("Base %s doesn't exist", baseCommit), e);
       }
 
       if (destRef == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
index d3e737f..b7bcaf9 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
@@ -207,7 +207,7 @@
     try {
       permissionBackend.currentUser().change(change).check(ChangePermission.READ);
     } catch (AuthException e) {
-      throw new UnprocessableEntityException("Read not permitted for " + baseChange);
+      throw new UnprocessableEntityException("Read not permitted for " + baseChange, e);
     }
     return psUtil.current(change);
   }
diff --git a/java/com/google/gerrit/server/restapi/change/GetDiff.java b/java/com/google/gerrit/server/restapi/change/GetDiff.java
index 3782605..41254ed 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDiff.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDiff.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.PatchScript;
 import com.google.gerrit.common.data.PatchScript.DisplayMethod;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
@@ -73,6 +74,8 @@
 import org.kohsuke.args4j.spi.Setter;
 
 public class GetDiff implements RestReadView<FileResource> {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
   private static final ImmutableMap<Patch.ChangeType, ChangeType> CHANGE_TYPE =
       Maps.immutableEnumMap(
           new ImmutableMap.Builder<Patch.ChangeType, ChangeType>()
@@ -433,6 +436,7 @@
             throw new NumberFormatException();
           }
         } catch (NumberFormatException e) {
+          logger.atFine().withCause(e).log("invalid numeric value");
           throw new CmdLineException(
               owner,
               localizable("\"%s\" is not a valid value for \"%s\""),
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index 9a843cd..4a98a9e 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -469,7 +469,8 @@
           throw new AuthException(
               String.format(
                   "not permitted to modify label \"%s\" on behalf of \"%s\"",
-                  type.getName(), in.onBehalfOf));
+                  type.getName(), in.onBehalfOf),
+              e);
         }
       }
     }
@@ -483,7 +484,7 @@
       permissionBackend.user(reviewer).change(rev.getNotes()).check(ChangePermission.READ);
     } catch (AuthException e) {
       throw new UnprocessableEntityException(
-          String.format("on_behalf_of account %s cannot see change", reviewer.getAccountId()));
+          String.format("on_behalf_of account %s cannot see change", reviewer.getAccountId()), e);
     }
 
     return new RevisionResource(
@@ -526,7 +527,7 @@
         perm.check(new LabelPermission.WithValue(lt, val));
       } catch (AuthException e) {
         throw new AuthException(
-            String.format("Applying label \"%s\": %d is restricted", lt.getName(), val));
+            String.format("Applying label \"%s\": %d is restricted", lt.getName(), val), e);
       }
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
index 7137b6e..c3cee0e 100644
--- a/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
+++ b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
@@ -173,7 +173,7 @@
           archiveFormat.putEntry(aos, path, bos.toByteArray());
         }
       } catch (LimitExceededException e) {
-        throw new NotImplementedException("The bundle is too big to generate at the server");
+        throw new NotImplementedException("The bundle is too big to generate at the server", e);
       } catch (NoSuchProjectException e) {
         throw new IOException(e);
       }
diff --git a/java/com/google/gerrit/server/restapi/change/PutAssignee.java b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
index de62725..a9a6f12 100644
--- a/java/com/google/gerrit/server/restapi/change/PutAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
@@ -89,7 +89,7 @@
           .change(rsrc.getNotes())
           .check(ChangePermission.READ);
     } catch (AuthException e) {
-      throw new AuthException("read not permitted for " + input.assignee);
+      throw new AuthException("read not permitted for " + input.assignee, e);
     }
 
     try (BatchUpdate bu =
diff --git a/java/com/google/gerrit/server/restapi/change/QueryChanges.java b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
index 5ee49ff..e1ed8d6 100644
--- a/java/com/google/gerrit/server/restapi/change/QueryChanges.java
+++ b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
@@ -119,7 +119,7 @@
     try {
       out = query();
     } catch (QueryRequiresAuthException e) {
-      throw new AuthException("Must be signed-in to use this operator");
+      throw new AuthException("Must be signed-in to use this operator", e);
     } catch (QueryParseException e) {
       logger.atFine().withCause(e).log("Reject change query with 400 Bad Request: %s", queries);
       throw new BadRequestException(e.getMessage(), e);
diff --git a/java/com/google/gerrit/server/restapi/change/Submit.java b/java/com/google/gerrit/server/restapi/change/Submit.java
index 9826f02..51e2dfb 100644
--- a/java/com/google/gerrit/server/restapi/change/Submit.java
+++ b/java/com/google/gerrit/server/restapi/change/Submit.java
@@ -214,7 +214,7 @@
     try {
       change = changeNotesFactory.createChecked(change.getProject(), change.getId()).getChange();
     } catch (NoSuchChangeException e) {
-      throw new ResourceConflictException("change is deleted");
+      throw new ResourceConflictException("change is deleted", e);
     }
 
     if (change.isMerged()) {
@@ -448,7 +448,7 @@
       permissionBackend.user(submitter).change(rsrc.getNotes()).check(ChangePermission.READ);
     } catch (AuthException e) {
       throw new UnprocessableEntityException(
-          String.format("on_behalf_of account %s cannot see change", submitter.getAccountId()));
+          String.format("on_behalf_of account %s cannot see change", submitter.getAccountId()), e);
     }
     return submitter;
   }
diff --git a/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java b/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
index 152a4db..b6dcd35 100644
--- a/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
+++ b/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
@@ -76,7 +76,7 @@
       }
       throw new UnprocessableEntityException("invalid token");
     } catch (EmailTokenVerifier.InvalidTokenException e) {
-      throw new UnprocessableEntityException("invalid token");
+      throw new UnprocessableEntityException("invalid token", e);
     } catch (AccountException e) {
       throw new UnprocessableEntityException(e.getMessage());
     }
diff --git a/java/com/google/gerrit/server/restapi/config/TasksCollection.java b/java/com/google/gerrit/server/restapi/config/TasksCollection.java
index dda54a0..9a7aa3a 100644
--- a/java/com/google/gerrit/server/restapi/config/TasksCollection.java
+++ b/java/com/google/gerrit/server/restapi/config/TasksCollection.java
@@ -81,7 +81,7 @@
     try {
       taskId = (int) Long.parseLong(id.get(), 16);
     } catch (NumberFormatException e) {
-      throw new ResourceNotFoundException(id);
+      throw new ResourceNotFoundException(id, e);
     }
 
     Task<?> task = workQueue.getTask(taskId);
diff --git a/java/com/google/gerrit/server/restapi/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java
index a841897..b2b14a1 100644
--- a/java/com/google/gerrit/server/restapi/group/AddMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java
@@ -137,7 +137,7 @@
     try {
       addMembers(groupUuid, newMemberIds);
     } catch (NoSuchGroupException e) {
-      throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
+      throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
     }
     return toAccountInfoList(newMemberIds);
   }
@@ -233,7 +233,7 @@
         }
         throw new IllegalStateException();
       } catch (UnprocessableEntityException e) {
-        throw new ResourceNotFoundException(id);
+        throw new ResourceNotFoundException(id, e);
       }
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
index 9f9deff..5c879e7 100644
--- a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
@@ -116,7 +116,7 @@
     try {
       addSubgroups(groupUuid, subgroupUuids);
     } catch (NoSuchGroupException e) {
-      throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
+      throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
     }
     return result;
   }
@@ -154,7 +154,7 @@
         }
         throw new IllegalStateException();
       } catch (UnprocessableEntityException e) {
-        throw new ResourceNotFoundException(id);
+        throw new ResourceNotFoundException(id, e);
       }
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/group/CreateGroup.java b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
index ffa462d..eaedcd7 100644
--- a/java/com/google/gerrit/server/restapi/group/CreateGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
@@ -219,7 +219,7 @@
       return groupsUpdateProvider.get().createGroup(groupCreation, groupUpdateBuilder.build());
     } catch (DuplicateKeyException e) {
       throw new ResourceConflictException(
-          "group '" + createGroupArgs.getGroupName() + "' already exists");
+          "group '" + createGroupArgs.getGroupName() + "' already exists", e);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
index b448631..b5771a3 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
@@ -74,7 +74,7 @@
     try {
       removeGroupMembers(groupUuid, membersToRemove);
     } catch (NoSuchGroupException e) {
-      throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
+      throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
     }
 
     return Response.none();
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
index 5821be5..9ecfa1f 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
@@ -77,7 +77,7 @@
     try {
       removeSubgroups(groupUuid, subgroupsToRemove);
     } catch (NoSuchGroupException e) {
-      throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
+      throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
     }
 
     return Response.none();
diff --git a/java/com/google/gerrit/server/restapi/group/GetOwner.java b/java/com/google/gerrit/server/restapi/group/GetOwner.java
index 0f0417e..10e1b23 100644
--- a/java/com/google/gerrit/server/restapi/group/GetOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/GetOwner.java
@@ -46,7 +46,7 @@
       GroupControl c = controlFactory.validateFor(group.getOwnerGroupUUID());
       return json.format(c.getGroup());
     } catch (NoSuchGroupException e) {
-      throw new ResourceNotFoundException();
+      throw new ResourceNotFoundException(group.getOwnerGroupUUID().get(), e);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/PutDescription.java b/java/com/google/gerrit/server/restapi/group/PutDescription.java
index c9078b0..c4e6f09 100644
--- a/java/com/google/gerrit/server/restapi/group/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/PutDescription.java
@@ -66,7 +66,7 @@
       try {
         groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
       } catch (NoSuchGroupException e) {
-        throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
+        throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
       }
     }
 
diff --git a/java/com/google/gerrit/server/restapi/group/PutName.java b/java/com/google/gerrit/server/restapi/group/PutName.java
index 319dc45..adc20d5 100644
--- a/java/com/google/gerrit/server/restapi/group/PutName.java
+++ b/java/com/google/gerrit/server/restapi/group/PutName.java
@@ -78,9 +78,9 @@
     try {
       groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
     } catch (NoSuchGroupException e) {
-      throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
+      throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
     } catch (DuplicateKeyException e) {
-      throw new ResourceConflictException("group with name " + newName + " already exists");
+      throw new ResourceConflictException("group with name " + newName + " already exists", e);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/PutOptions.java b/java/com/google/gerrit/server/restapi/group/PutOptions.java
index 267f414..747d899 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOptions.java
@@ -65,7 +65,7 @@
       try {
         groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
       } catch (NoSuchGroupException e) {
-        throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
+        throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
       }
     }
 
diff --git a/java/com/google/gerrit/server/restapi/group/PutOwner.java b/java/com/google/gerrit/server/restapi/group/PutOwner.java
index 4cdad38..f766a84 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOwner.java
@@ -76,7 +76,7 @@
       try {
         groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
       } catch (NoSuchGroupException e) {
-        throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
+        throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
       }
     }
     return json.format(owner);
diff --git a/java/com/google/gerrit/server/restapi/project/BranchesCollection.java b/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
index 2b7e089..5fbb4f8 100644
--- a/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
@@ -82,9 +82,9 @@
           .check(RefPermission.READ);
       return new BranchResource(parent.getProjectState(), parent.getUser(), ref);
     } catch (AuthException notAllowed) {
-      throw new ResourceNotFoundException(id);
+      throw new ResourceNotFoundException(id, notAllowed);
     } catch (RepositoryNotFoundException noRepo) {
-      throw new ResourceNotFoundException();
+      throw new ResourceNotFoundException(id, noRepo);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
index 25bdadb..d6c4469 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
@@ -80,7 +80,7 @@
     try {
       objectId = ObjectId.fromString(id.get());
     } catch (IllegalArgumentException e) {
-      throw new ResourceNotFoundException(id);
+      throw new ResourceNotFoundException(id, e);
     }
 
     try (Repository repo = repoManager.openRepository(parent.getNameKey());
@@ -95,7 +95,7 @@
       }
       return new CommitResource(parent, commit);
     } catch (MissingObjectException | IncorrectObjectTypeException e) {
-      throw new ResourceNotFoundException(id);
+      throw new ResourceNotFoundException(id, e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
index ee3d0a8..0741377 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
@@ -102,7 +102,7 @@
       try {
         forProject.ref(RefNames.REFS_CONFIG).check(RefPermission.CREATE_CHANGE);
       } catch (AuthException denied) {
-        throw new AuthException("cannot create change for " + RefNames.REFS_CONFIG);
+        throw new AuthException("cannot create change for " + RefNames.REFS_CONFIG, denied);
       }
     }
     projectCache.checkedGet(rsrc.getNameKey()).checkStatePermitsWrite();
diff --git a/java/com/google/gerrit/server/restapi/project/CreateBranch.java b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
index ec1f56e..01e0cdf 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
@@ -125,7 +125,7 @@
         try {
           object = rw.parseCommit(object);
         } catch (IncorrectObjectTypeException notCommit) {
-          throw new BadRequestException("\"" + input.revision + "\" not a commit");
+          throw new BadRequestException("\"" + input.revision + "\" not a commit", notCommit);
         }
       }
 
@@ -198,7 +198,7 @@
         throw err;
       }
     } catch (RefUtil.InvalidRevisionException e) {
-      throw new BadRequestException("invalid revision \"" + input.revision + "\"");
+      throw new BadRequestException("invalid revision \"" + input.revision + "\"", e);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/CreateTag.java b/java/com/google/gerrit/server/restapi/project/CreateTag.java
index 855a7c7..7493d24 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateTag.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateTag.java
@@ -147,7 +147,7 @@
         }
       }
     } catch (InvalidRevisionException e) {
-      throw new BadRequestException("Invalid base revision");
+      throw new BadRequestException("Invalid base revision", e);
     } catch (GitAPIException e) {
       logger.atSevere().withCause(e).log("Cannot create tag \"%s\"", ref);
       throw new IOException(e);
diff --git a/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java b/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java
index 07691e7..4dc83e8 100644
--- a/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java
@@ -103,14 +103,14 @@
     try {
       info = newDashboardInfo(id.get());
     } catch (InvalidDashboardId e) {
-      throw new ResourceNotFoundException(id);
+      throw new ResourceNotFoundException(id, e);
     }
 
     for (ProjectState ps : parent.getProjectState().tree()) {
       try {
         return parse(ps, parent.getProjectState(), parent.getUser(), info);
       } catch (AmbiguousObjectException | ConfigInvalidException | IncorrectObjectTypeException e) {
-        throw new ResourceNotFoundException(id);
+        throw new ResourceNotFoundException(id, e);
       } catch (ResourceNotFoundException e) {
         continue;
       }
@@ -135,7 +135,7 @@
       permissionBackend.user(user).project(parent.getNameKey()).ref(ref).check(RefPermission.READ);
     } catch (AuthException e) {
       // Don't leak the project's existence
-      throw new ResourceNotFoundException(info.id);
+      throw new ResourceNotFoundException(info.id, e);
     }
     if (!Repository.isValidRefName(ref)) {
       throw new ResourceNotFoundException(info.id);
@@ -151,7 +151,7 @@
       BlobBasedConfig cfg = new BlobBasedConfig(null, git, objId);
       return new DashboardResource(current, user, ref, info.path, cfg, false);
     } catch (RepositoryNotFoundException e) {
-      throw new ResourceNotFoundException(info.id);
+      throw new ResourceNotFoundException(info.id, e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/project/GetAccess.java b/java/com/google/gerrit/server/restapi/project/GetAccess.java
index e53f474..d6c07dd 100644
--- a/java/com/google/gerrit/server/restapi/project/GetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/GetAccess.java
@@ -166,7 +166,7 @@
     } catch (ConfigInvalidException e) {
       throw new ResourceConflictException(e.getMessage());
     } catch (RepositoryNotFoundException e) {
-      throw new ResourceNotFoundException(rsrc.getName());
+      throw new ResourceNotFoundException(rsrc.getName(), e);
     }
 
     // The following implementation must match the ProjectAccessFactory JSON RPC endpoint.
diff --git a/java/com/google/gerrit/server/restapi/project/GetHead.java b/java/com/google/gerrit/server/restapi/project/GetHead.java
index bc267c8..043991f 100644
--- a/java/com/google/gerrit/server/restapi/project/GetHead.java
+++ b/java/com/google/gerrit/server/restapi/project/GetHead.java
@@ -81,13 +81,13 @@
                 .project(rsrc.getNameKey())
                 .check(ProjectPermission.WRITE_CONFIG);
           } catch (AuthException ae) {
-            throw new AuthException("not allowed to see HEAD");
+            throw new AuthException("not allowed to see HEAD", ae);
           }
         }
       }
       throw new ResourceNotFoundException(Constants.HEAD);
     } catch (RepositoryNotFoundException e) {
-      throw new ResourceNotFoundException(rsrc.getName());
+      throw new ResourceNotFoundException(rsrc.getName(), e);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/GetReflog.java b/java/com/google/gerrit/server/restapi/project/GetReflog.java
index 4b9a489..a690042 100644
--- a/java/com/google/gerrit/server/restapi/project/GetReflog.java
+++ b/java/com/google/gerrit/server/restapi/project/GetReflog.java
@@ -103,7 +103,7 @@
       } catch (UnsupportedOperationException e) {
         String msg = "reflog not supported on repo " + rsrc.getNameKey().get();
         logger.atSevere().log(msg);
-        throw new MethodNotAllowedException(msg);
+        throw new MethodNotAllowedException(msg, e);
       }
       if (r == null) {
         throw new ResourceNotFoundException(rsrc.getRef());
diff --git a/java/com/google/gerrit/server/restapi/project/GetStatistics.java b/java/com/google/gerrit/server/restapi/project/GetStatistics.java
index 048c018..a408062 100644
--- a/java/com/google/gerrit/server/restapi/project/GetStatistics.java
+++ b/java/com/google/gerrit/server/restapi/project/GetStatistics.java
@@ -50,7 +50,7 @@
     } catch (GitAPIException | JGitInternalException e) {
       throw new ResourceConflictException(e.getMessage());
     } catch (IOException e) {
-      throw new ResourceNotFoundException(rsrc.getName());
+      throw new ResourceNotFoundException(rsrc.getName(), e);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/ListBranches.java b/java/com/google/gerrit/server/restapi/project/ListBranches.java
index a0d2528..ae9ef28 100644
--- a/java/com/google/gerrit/server/restapi/project/ListBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/ListBranches.java
@@ -147,7 +147,7 @@
       }
       return toBranchInfo(rsrc, ImmutableList.of(r)).get(0);
     } catch (RepositoryNotFoundException noRepo) {
-      throw new ResourceNotFoundException();
+      throw new ResourceNotFoundException(rsrc.getNameKey().get(), noRepo);
     }
   }
 
@@ -163,7 +163,7 @@
               .exactRef(Constants.HEAD, RefNames.REFS_CONFIG, RefNames.REFS_USERS_DEFAULT)
               .values());
     } catch (RepositoryNotFoundException noGitRepository) {
-      throw new ResourceNotFoundException();
+      throw new ResourceNotFoundException(rsrc.getNameKey().get(), noGitRepository);
     }
     return toBranchInfo(rsrc, refs);
   }
diff --git a/java/com/google/gerrit/server/restapi/project/ListDashboards.java b/java/com/google/gerrit/server/restapi/project/ListDashboards.java
index 3808a2f..fd8668a 100644
--- a/java/com/google/gerrit/server/restapi/project/ListDashboards.java
+++ b/java/com/google/gerrit/server/restapi/project/ListDashboards.java
@@ -119,7 +119,7 @@
       }
       return all;
     } catch (RepositoryNotFoundException e) {
-      throw new ResourceNotFoundException();
+      throw new ResourceNotFoundException(project, e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/project/ListTags.java b/java/com/google/gerrit/server/restapi/project/ListTags.java
index 98c8c61..08b9b84 100644
--- a/java/com/google/gerrit/server/restapi/project/ListTags.java
+++ b/java/com/google/gerrit/server/restapi/project/ListTags.java
@@ -217,7 +217,7 @@
     try {
       return repoManager.openRepository(project);
     } catch (RepositoryNotFoundException noGitRepository) {
-      throw new ResourceNotFoundException();
+      throw new ResourceNotFoundException(project.get(), noGitRepository);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/project/PutConfig.java b/java/com/google/gerrit/server/restapi/project/PutConfig.java
index 150989b..56ddbb4 100644
--- a/java/com/google/gerrit/server/restapi/project/PutConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/PutConfig.java
@@ -178,7 +178,7 @@
           uiActions,
           views);
     } catch (RepositoryNotFoundException notFound) {
-      throw new ResourceNotFoundException(projectName.get());
+      throw new ResourceNotFoundException(projectName.get(), notFound);
     } catch (ConfigInvalidException err) {
       throw new ResourceConflictException("Cannot read project " + projectName, err);
     } catch (IOException err) {
diff --git a/java/com/google/gerrit/server/restapi/project/PutDescription.java b/java/com/google/gerrit/server/restapi/project/PutDescription.java
index b65f0d3..c3a063d 100644
--- a/java/com/google/gerrit/server/restapi/project/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/project/PutDescription.java
@@ -92,7 +92,7 @@
           ? Response.none()
           : Response.ok(project.getDescription());
     } catch (RepositoryNotFoundException notFound) {
-      throw new ResourceNotFoundException(resource.getName());
+      throw new ResourceNotFoundException(resource.getName(), notFound);
     } catch (ConfigInvalidException e) {
       throw new ResourceConflictException(
           String.format("invalid project.config: %s", e.getMessage()));
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccess.java b/java/com/google/gerrit/server/restapi/project/SetAccess.java
index 5277cf8..95bc75f7 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccess.java
@@ -135,7 +135,7 @@
     } catch (InvalidNameException e) {
       throw new BadRequestException(e.toString());
     } catch (ConfigInvalidException e) {
-      throw new ResourceConflictException(rsrc.getName());
+      throw new ResourceConflictException(rsrc.getName(), e);
     }
 
     return getAccess.apply(rsrc.getNameKey());
diff --git a/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
index 0f346df..3917bee 100644
--- a/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
@@ -89,7 +89,7 @@
                 new ProjectResource(rsrc.getProjectState(), rsrc.getUser()),
                 IdString.fromUrl(input.id));
       } catch (ResourceNotFoundException e) {
-        throw new BadRequestException("dashboard " + input.id + " not found");
+        throw new BadRequestException("dashboard " + input.id + " not found", e);
       } catch (ConfigInvalidException e) {
         throw new ResourceConflictException(e.getMessage());
       }
@@ -125,7 +125,7 @@
       }
       return Response.none();
     } catch (RepositoryNotFoundException notFound) {
-      throw new ResourceNotFoundException(rsrc.getProjectState().getProject().getName());
+      throw new ResourceNotFoundException(rsrc.getProjectState().getProject().getName(), notFound);
     } catch (ConfigInvalidException e) {
       throw new ResourceConflictException(
           String.format("invalid project.config: %s", e.getMessage()));
diff --git a/java/com/google/gerrit/server/restapi/project/SetHead.java b/java/com/google/gerrit/server/restapi/project/SetHead.java
index b310f16..430d709 100644
--- a/java/com/google/gerrit/server/restapi/project/SetHead.java
+++ b/java/com/google/gerrit/server/restapi/project/SetHead.java
@@ -111,7 +111,7 @@
       }
       return ref;
     } catch (RepositoryNotFoundException e) {
-      throw new ResourceNotFoundException(rsrc.getName());
+      throw new ResourceNotFoundException(rsrc.getName(), e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/project/SetParent.java b/java/com/google/gerrit/server/restapi/project/SetParent.java
index 7856c09..2d9ef42 100644
--- a/java/com/google/gerrit/server/restapi/project/SetParent.java
+++ b/java/com/google/gerrit/server/restapi/project/SetParent.java
@@ -120,7 +120,7 @@
       requireNonNull(parent);
       return parent.get();
     } catch (RepositoryNotFoundException notFound) {
-      throw new ResourceNotFoundException(rsrc.getName());
+      throw new ResourceNotFoundException(rsrc.getName(), notFound);
     } catch (ConfigInvalidException e) {
       throw new ResourceConflictException(
           String.format("invalid project.config: %s", e.getMessage()));
diff --git a/java/com/google/gerrit/server/rules/StoredValues.java b/java/com/google/gerrit/server/rules/StoredValues.java
index 9bae091..30d329d 100644
--- a/java/com/google/gerrit/server/rules/StoredValues.java
+++ b/java/com/google/gerrit/server/rules/StoredValues.java
@@ -58,7 +58,8 @@
     try {
       return cd.change();
     } catch (StorageException e) {
-      throw new SystemException("Cannot load change " + cd.getId());
+      throw new SystemException(
+          String.format("Cannot load change %s: %s", cd.getId(), e.getMessage()));
     }
   }
 
@@ -103,7 +104,7 @@
           try {
             patchList = plCache.get(plKey, project);
           } catch (PatchListNotAvailableException e) {
-            throw new SystemException("Cannot create " + plKey);
+            throw new SystemException(String.format("Cannot create %s: %s", plKey, e.getMessage()));
           }
           return patchList;
         }
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
index 3d08a73..df95ff7 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
@@ -104,7 +104,7 @@
     try {
       schemaCreator.ensureCreated();
     } catch (IOException | ConfigInvalidException e) {
-      throw new StorageException("Cannot initialize Gerrit site");
+      throw new StorageException("Cannot initialize Gerrit site", e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
index 342ae69..b2ad4e2 100644
--- a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
@@ -161,7 +161,8 @@
         } catch (MergeConflictException mce) {
           // Unlike in Cherry-pick case, this should never happen.
           toMerge.setStatusCode(CommitMergeStatus.REBASE_MERGE_CONFLICT);
-          throw new IllegalStateException("MergeConflictException on message edit must not happen");
+          throw new IllegalStateException(
+              "MergeConflictException on message edit must not happen", mce);
         } catch (MergeIdenticalTreeException mie) {
           // this should not happen
           toMerge.setStatusCode(SKIPPED_IDENTICAL_TREE);
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
index 73bcc09..14522a2 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
@@ -549,7 +549,7 @@
       return args.submoduleOp.composeGitlinksCommit(args.destBranch, commit);
     } catch (SubmoduleException | IOException e) {
       throw new IntegrationException(
-          "cannot update gitlink for the commit at branch: " + args.destBranch);
+          "cannot update gitlink for the commit at branch: " + args.destBranch, e);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/util/SocketUtil.java b/java/com/google/gerrit/server/util/SocketUtil.java
index afa2aee..e617650 100644
--- a/java/com/google/gerrit/server/util/SocketUtil.java
+++ b/java/com/google/gerrit/server/util/SocketUtil.java
@@ -96,7 +96,7 @@
       try {
         port = Integer.parseInt(portStr);
       } catch (NumberFormatException e) {
-        throw new IllegalArgumentException("invalid port: " + desc);
+        throw new IllegalArgumentException("invalid port: " + desc, e);
       }
     } else {
       port = defaultPort;
diff --git a/java/com/google/gerrit/sshd/BaseCommand.java b/java/com/google/gerrit/sshd/BaseCommand.java
index 2081967..472f1c7 100644
--- a/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/java/com/google/gerrit/sshd/BaseCommand.java
@@ -385,6 +385,10 @@
     return new UnloggedFailure(1, "fatal: " + msg);
   }
 
+  protected UnloggedFailure die(String msg, Throwable why) {
+    return new UnloggedFailure(1, "fatal: " + msg, why);
+  }
+
   protected UnloggedFailure die(Throwable why) {
     return new UnloggedFailure(1, "fatal: " + why.getMessage(), why);
   }
diff --git a/java/com/google/gerrit/sshd/ChangeArgumentParser.java b/java/com/google/gerrit/sshd/ChangeArgumentParser.java
index 0b73bbf..ed6f87f 100644
--- a/java/com/google/gerrit/sshd/ChangeArgumentParser.java
+++ b/java/com/google/gerrit/sshd/ChangeArgumentParser.java
@@ -111,7 +111,7 @@
     try {
       changeResource = changesCollection.parse(cId);
     } catch (RestApiException e) {
-      throw new UnloggedFailure(1, "\"" + id + "\" no such change");
+      throw new UnloggedFailure(1, "\"" + id + "\" no such change", e);
     }
     changes.put(cId, changeResource);
   }
diff --git a/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java b/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
index da0ec1d..b2853b9 100644
--- a/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
+++ b/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
@@ -34,11 +34,10 @@
       SshUtil.parse(key);
       return key;
     } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
-      throw new InvalidSshKeyException();
-
+      throw new InvalidSshKeyException(e);
     } catch (NoSuchProviderException e) {
       logger.atSevere().withCause(e).log("Cannot parse SSH key");
-      throw new InvalidSshKeyException();
+      throw new InvalidSshKeyException(e);
     }
   }
 }
diff --git a/java/com/google/gerrit/sshd/SuExec.java b/java/com/google/gerrit/sshd/SuExec.java
index 7053a0d..6d0bf2b 100644
--- a/java/com/google/gerrit/sshd/SuExec.java
+++ b/java/com/google/gerrit/sshd/SuExec.java
@@ -126,9 +126,9 @@
       try {
         permissionBackend.user(caller).check(GlobalPermission.RUN_AS);
       } catch (AuthException e) {
-        throw die("suexec not permitted");
+        throw die("suexec not permitted", e);
       } catch (PermissionBackendException e) {
-        throw die("suexec not available: " + e);
+        throw die("suexec not available", e);
       }
     }
   }
diff --git a/java/com/google/gerrit/sshd/commands/LsUserRefs.java b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
index db3e649..f26a4fa 100644
--- a/java/com/google/gerrit/sshd/commands/LsUserRefs.java
+++ b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
@@ -104,9 +104,9 @@
         throw new Failure(1, "fatal: Error reading refs: '" + projectName, e);
       }
     } catch (RepositoryNotFoundException e) {
-      throw die("'" + projectName + "': not a git archive");
+      throw die("'" + projectName + "': not a git archive", e);
     } catch (IOException e) {
-      throw die("Error opening: '" + projectName);
+      throw die("Error opening: '" + projectName, e);
     }
   }
 }
diff --git a/java/com/google/gerrit/sshd/commands/PatchSetParser.java b/java/com/google/gerrit/sshd/commands/PatchSetParser.java
index a6e802a..45210f6 100644
--- a/java/com/google/gerrit/sshd/commands/PatchSetParser.java
+++ b/java/com/google/gerrit/sshd/commands/PatchSetParser.java
@@ -99,7 +99,7 @@
       try {
         patchSetId = PatchSet.Id.parse(token);
       } catch (IllegalArgumentException e) {
-        throw error("\"" + token + "\" is not a valid patch set");
+        throw error("\"" + token + "\" is not a valid patch set", e);
       }
       ChangeNotes notes = getNotes(projectState, patchSetId.getParentKey());
       PatchSet patchSet = psUtil.get(notes, patchSetId);
@@ -130,7 +130,7 @@
       ChangeNotes notes = changeFinder.findOne(changeId);
       return notesFactory.create(notes.getProjectName(), changeId);
     } catch (NoSuchChangeException e) {
-      throw error("\"" + changeId + "\" no such change");
+      throw error("\"" + changeId + "\" no such change", e);
     }
   }
 
@@ -153,4 +153,8 @@
   public static UnloggedFailure error(String msg) {
     return new UnloggedFailure(1, msg);
   }
+
+  public static UnloggedFailure error(String msg, Throwable why) {
+    return new UnloggedFailure(1, msg, why);
+  }
 }
diff --git a/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java b/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
index 8b045ec..cfb47f7 100644
--- a/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
+++ b/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
@@ -70,21 +70,21 @@
       try {
         data = Files.newInputStream(new File(source).toPath());
       } catch (IOException e) {
-        throw die("cannot read " + source);
+        throw die("cannot read " + source, e);
       }
     } else {
       try {
         data = new URL(source).openStream();
       } catch (MalformedURLException e) {
-        throw die("invalid url " + source);
+        throw die("invalid url " + source, e);
       } catch (IOException e) {
-        throw die("cannot read " + source);
+        throw die("cannot read " + source, e);
       }
     }
     try {
       loader.installPluginFromStream(name, data);
     } catch (IOException e) {
-      throw die("cannot install plugin");
+      throw die("cannot install plugin", e);
     } catch (PluginInstallException e) {
       e.printStackTrace(stderr);
       String msg = String.format("Plugin failed to install. Cause: %s", e.getMessage());
diff --git a/java/com/google/gerrit/sshd/commands/Receive.java b/java/com/google/gerrit/sshd/commands/Receive.java
index fa0e37b..53a9ca2 100644
--- a/java/com/google/gerrit/sshd/commands/Receive.java
+++ b/java/com/google/gerrit/sshd/commands/Receive.java
@@ -83,7 +83,7 @@
           .project(project.getNameKey())
           .check(ProjectPermission.RUN_RECEIVE_PACK);
     } catch (AuthException e) {
-      throw new Failure(1, "fatal: receive-pack not permitted on this server");
+      throw new Failure(1, "fatal: receive-pack not permitted on this server", e);
     } catch (PermissionBackendException e) {
       throw new Failure(1, "fatal: unable to check permissions " + e);
     }
diff --git a/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 7af07de..0af8936 100644
--- a/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -326,7 +326,7 @@
     try {
       allProjectsState = projectCache.checkedGet(allProjects);
     } catch (IOException e) {
-      throw die("missing " + allProjects.get());
+      throw die("missing " + allProjects.get(), e);
     }
 
     for (LabelType type : allProjectsState.getLabelTypes().getLabelTypes()) {
diff --git a/java/com/google/gerrit/sshd/commands/Upload.java b/java/com/google/gerrit/sshd/commands/Upload.java
index 24a6975..6b5dd75 100644
--- a/java/com/google/gerrit/sshd/commands/Upload.java
+++ b/java/com/google/gerrit/sshd/commands/Upload.java
@@ -55,9 +55,9 @@
 
       perm.check(ProjectPermission.RUN_UPLOAD_PACK);
     } catch (AuthException e) {
-      throw new Failure(1, "fatal: upload-pack not permitted on this server");
+      throw new Failure(1, "fatal: upload-pack not permitted on this server", e);
     } catch (PermissionBackendException e) {
-      throw new Failure(1, "fatal: unable to check permissions " + e);
+      throw new Failure(1, "fatal: unable to check permissions ", e);
     }
 
     final UploadPack up = new UploadPack(repo);
diff --git a/java/com/google/gwtorm/client/StandardKeyEncoder.java b/java/com/google/gwtorm/client/StandardKeyEncoder.java
index d6d503a..1ceb0dc 100644
--- a/java/com/google/gwtorm/client/StandardKeyEncoder.java
+++ b/java/com/google/gwtorm/client/StandardKeyEncoder.java
@@ -100,7 +100,7 @@
         }
       }
     } catch (ArrayIndexOutOfBoundsException err) {
-      throw new IllegalArgumentException("Bad encoding: " + e);
+      throw new IllegalArgumentException("Bad encoding" + e, err);
     }
     try {
       return new String(b, 0, bPtr, "UTF-8");
diff --git a/tools/BUILD b/tools/BUILD
index 52493a8..6876945 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -83,6 +83,7 @@
         "-Xep:TypeParameterUnusedInFormals:WARN",
         "-Xep:URLEqualsHashCode:WARN",
         "-Xep:UnsynchronizedOverridesSynchronized:WARN",
+        "-Xep:UnusedException:ERROR",
         "-Xep:WaitNotInLoop:WARN",
     ],
     packages = ["error_prone_packages"],