Make FileSymlinkException and InconsistentFSException IOExceptions

Most places handle them the same way as IOException, which seems like a safe
default. The places that do care can still throw or catch the more specific type.

PiperOrigin-RevId: 181719688
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
index de823aa..c27d108 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
@@ -32,9 +32,7 @@
 import com.google.devtools.build.lib.rules.repository.RepositoryFunction;
 import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException;
 import com.google.devtools.build.lib.rules.repository.WorkspaceAttributeMapper;
-import com.google.devtools.build.lib.skyframe.FileSymlinkException;
 import com.google.devtools.build.lib.skyframe.FileValue;
-import com.google.devtools.build.lib.skyframe.InconsistentFilesystemException;
 import com.google.devtools.build.lib.skylarkinterface.Param;
 import com.google.devtools.build.lib.skylarkinterface.ParamType;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
@@ -731,9 +729,8 @@
     SkyKey fileSkyKey = FileValue.key(rootedPath);
     FileValue fileValue = null;
     try {
-      fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class,
-          FileSymlinkException.class, InconsistentFilesystemException.class);
-    } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
+      fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class);
+    } catch (IOException e) {
       throw new EvalException(Location.BUILTIN, e);
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java
index 58d51e5..26b63c7 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java
@@ -32,9 +32,7 @@
 import com.google.devtools.build.lib.rules.repository.RepositoryDirectoryValue;
 import com.google.devtools.build.lib.rules.repository.WorkspaceAttributeMapper;
 import com.google.devtools.build.lib.skyframe.DirectoryListingValue;
-import com.google.devtools.build.lib.skyframe.FileSymlinkException;
 import com.google.devtools.build.lib.skyframe.FileValue;
-import com.google.devtools.build.lib.skyframe.InconsistentFilesystemException;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.util.ResourceFileLoader;
@@ -364,15 +362,11 @@
 
     String releaseFileContent = "";
     try {
-      env.getValueOrThrow(
-          releaseFileKey,
-          IOException.class,
-          FileSymlinkException.class,
-          InconsistentFilesystemException.class);
+      env.getValueOrThrow(releaseFileKey, IOException.class);
 
       releaseFileContent =
           new String(FileSystemUtils.readContent(releaseFilePath), StandardCharsets.UTF_8);
-    } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
+    } catch (IOException e) {
       throwInvalidPathException(
           directory,
           new IOException(
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidRepositoryFunction.java
index 4d232a9..846653a 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidRepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidRepositoryFunction.java
@@ -17,7 +17,6 @@
 import com.google.devtools.build.lib.rules.repository.RepositoryFunction;
 import com.google.devtools.build.lib.skyframe.DirectoryListingValue;
 import com.google.devtools.build.lib.skyframe.Dirents;
-import com.google.devtools.build.lib.skyframe.FileSymlinkException;
 import com.google.devtools.build.lib.skyframe.FileValue;
 import com.google.devtools.build.lib.skyframe.InconsistentFilesystemException;
 import com.google.devtools.build.lib.vfs.Dirent;
@@ -53,12 +52,7 @@
     RootedPath rootedPath = RootedPath.toRootedPath(root, dirPath);
     try {
       FileValue dirFileValue =
-          (FileValue)
-              env.getValueOrThrow(
-                  FileValue.key(rootedPath),
-                  IOException.class,
-                  FileSymlinkException.class,
-                  InconsistentFilesystemException.class);
+          (FileValue) env.getValueOrThrow(FileValue.key(rootedPath), IOException.class);
       if (dirFileValue == null) {
         return null;
       }
@@ -76,8 +70,6 @@
               InconsistentFilesystemException.class);
     } catch (IOException e) {
       throw new RepositoryFunctionException(e, Transience.PERSISTENT);
-    } catch (FileSymlinkException | InconsistentFilesystemException e) {
-      throw new RepositoryFunctionException(new IOException(e), Transience.PERSISTENT);
     }
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java
index de58dbf..e888f73 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java
@@ -29,7 +29,6 @@
 import com.google.devtools.build.lib.rules.repository.WorkspaceAttributeMapper;
 import com.google.devtools.build.lib.skyframe.DirectoryListingValue;
 import com.google.devtools.build.lib.skyframe.Dirents;
-import com.google.devtools.build.lib.skyframe.FileSymlinkException;
 import com.google.devtools.build.lib.skyframe.FileValue;
 import com.google.devtools.build.lib.skyframe.InconsistentFilesystemException;
 import com.google.devtools.build.lib.syntax.EvalException;
@@ -308,16 +307,13 @@
         RootedPath.toRootedPath(directory, sourcePropertiesFilePath));
 
     try {
-      env.getValueOrThrow(releaseFileKey,
-          IOException.class,
-          FileSymlinkException.class,
-          InconsistentFilesystemException.class);
+      env.getValueOrThrow(releaseFileKey, IOException.class);
 
       Properties properties = new Properties();
       properties.load(sourcePropertiesFilePath.getInputStream());
       return properties;
 
-    } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
+    } catch (IOException e) {
       String error = String.format(
           "Could not read %s in Android SDK: %s", sourcePropertiesFilePath, e.getMessage());
       throw new RepositoryFunctionException(new IOException(error), Transience.PERSISTENT);
@@ -421,7 +417,7 @@
         }
         directoryListingValues.put(pathFragment, (DirectoryListingValue) skyValue);
       } catch (InconsistentFilesystemException e) {
-        throw new RepositoryFunctionException(new IOException(e), Transience.PERSISTENT);
+        throw new RepositoryFunctionException(e, Transience.PERSISTENT);
       }
     }
     return directoryListingValues.build();
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/LocalRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/rules/repository/LocalRepositoryFunction.java
index f9b520c..5b8f088 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/LocalRepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/LocalRepositoryFunction.java
@@ -18,9 +18,7 @@
 import com.google.devtools.build.lib.analysis.RuleDefinition;
 import com.google.devtools.build.lib.packages.BuildFileName;
 import com.google.devtools.build.lib.packages.Rule;
-import com.google.devtools.build.lib.skyframe.FileSymlinkException;
 import com.google.devtools.build.lib.skyframe.FileValue;
-import com.google.devtools.build.lib.skyframe.InconsistentFilesystemException;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.RootedPath;
@@ -103,14 +101,8 @@
     SkyKey workspaceFileKey = FileValue.key(workspaceRootedFile);
     FileValue value;
     try {
-      value =
-          (FileValue)
-              env.getValueOrThrow(
-                  workspaceFileKey,
-                  IOException.class,
-                  FileSymlinkException.class,
-                  InconsistentFilesystemException.class);
-    } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
+      value = (FileValue) env.getValueOrThrow(workspaceFileKey, IOException.class);
+    } catch (IOException e) {
       throw new RepositoryFunctionException(
           new IOException("Could not access " + workspaceRootedFile + ": " + e.getMessage()),
           Transience.PERSISTENT);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/NewLocalRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/rules/repository/NewLocalRepositoryFunction.java
index 1e2a531..9811287 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/NewLocalRepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/NewLocalRepositoryFunction.java
@@ -18,7 +18,6 @@
 import com.google.devtools.build.lib.analysis.RuleDefinition;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.skyframe.DirectoryListingValue;
-import com.google.devtools.build.lib.skyframe.FileSymlinkException;
 import com.google.devtools.build.lib.skyframe.FileValue;
 import com.google.devtools.build.lib.skyframe.InconsistentFilesystemException;
 import com.google.devtools.build.lib.vfs.FileSystem;
@@ -61,12 +60,7 @@
 
     try {
       FileValue dirFileValue =
-          (FileValue)
-              env.getValueOrThrow(
-                  FileValue.key(dirPath),
-                  IOException.class,
-                  FileSymlinkException.class,
-                  InconsistentFilesystemException.class);
+          (FileValue) env.getValueOrThrow(FileValue.key(dirPath), IOException.class);
       if (dirFileValue == null) {
         return null;
       }
@@ -90,10 +84,6 @@
       }
     } catch (IOException e) {
       throw new RepositoryFunctionException(e, Transience.PERSISTENT);
-    } catch (FileSymlinkException e) {
-      throw new RepositoryFunctionException(new IOException(e), Transience.PERSISTENT);
-    } catch (InconsistentFilesystemException e) {
-      throw new RepositoryFunctionException(new IOException(e), Transience.PERSISTENT);
     }
 
     // fetch() creates symlinks to each child under 'path' and DiffAwareness handles checking all
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/NewRepositoryFileHandler.java b/src/main/java/com/google/devtools/build/lib/rules/repository/NewRepositoryFileHandler.java
index 6317a65..7d93da6f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/NewRepositoryFileHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/NewRepositoryFileHandler.java
@@ -19,9 +19,7 @@
 import com.google.devtools.build.lib.cmdline.LabelValidator;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException;
-import com.google.devtools.build.lib.skyframe.FileSymlinkException;
 import com.google.devtools.build.lib.skyframe.FileValue;
-import com.google.devtools.build.lib.skyframe.InconsistentFilesystemException;
 import com.google.devtools.build.lib.skyframe.PackageLookupValue;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.Type;
@@ -252,17 +250,11 @@
         // don't write to things in the file system this FileValue depends on. In theory, the latter
         // is possible if the file referenced by workspace_file is a symlink to somewhere under the
         // external/ directory, but if you do that, you are really asking for trouble.
-        fileValue =
-            (FileValue)
-                env.getValueOrThrow(
-                    fileKey,
-                    IOException.class,
-                    FileSymlinkException.class,
-                    InconsistentFilesystemException.class);
+        fileValue = (FileValue) env.getValueOrThrow(fileKey, IOException.class);
         if (fileValue == null) {
           return null;
         }
-      } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
+      } catch (IOException e) {
         throw new RepositoryFunctionException(
             new IOException("Cannot lookup " + fileAttribute + ": " + e.getMessage()),
             Transience.TRANSIENT);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java
index 5e74aad..0cf44e6 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java
@@ -35,9 +35,7 @@
 import com.google.devtools.build.lib.repository.ExternalRuleNotFoundException;
 import com.google.devtools.build.lib.skyframe.ActionEnvironmentFunction;
 import com.google.devtools.build.lib.skyframe.FileStateValue.RegularFileStateValue;
-import com.google.devtools.build.lib.skyframe.FileSymlinkException;
 import com.google.devtools.build.lib.skyframe.FileValue;
-import com.google.devtools.build.lib.skyframe.InconsistentFilesystemException;
 import com.google.devtools.build.lib.skyframe.PackageLookupValue;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.Type;
@@ -218,13 +216,7 @@
       }
 
       SkyKey fileSkyKey = FileValue.key(rootedPath);
-      FileValue fileValue =
-          (FileValue)
-              env.getValueOrThrow(
-                  fileSkyKey,
-                  IOException.class,
-                  FileSymlinkException.class,
-                  InconsistentFilesystemException.class);
+      FileValue fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class);
 
       if (fileValue == null || !fileValue.isFile() || fileValue.isSpecialFile()) {
         return false;
@@ -234,10 +226,7 @@
     } catch (LabelSyntaxException e) {
       throw new IllegalStateException(
           "Key " + key + " is not a correct file key (should be in form FILE:label)", e);
-    } catch (IOException
-        | FileSymlinkException
-        | InconsistentFilesystemException
-        | EvalException e) {
+    } catch (IOException | EvalException e) {
       // Consider those exception to be a cause for invalidation
       return false;
     }
@@ -488,9 +477,8 @@
         repositoryDirectory, PathFragment.EMPTY_FRAGMENT));
     FileValue value;
     try {
-      value = (FileValue) env.getValueOrThrow(outputDirectoryKey, IOException.class,
-          FileSymlinkException.class, InconsistentFilesystemException.class);
-    } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
+      value = (FileValue) env.getValueOrThrow(outputDirectoryKey, IOException.class);
+    } catch (IOException e) {
       throw new RepositoryFunctionException(
           new IOException("Could not access " + repositoryDirectory + ": " + e.getMessage()),
           Transience.PERSISTENT);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
index 0520c02..2fcbfbe 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
@@ -84,16 +84,12 @@
     SkyKey fileSkyKey = FileValue.key(rootedPath);
     FileValue fileValue = null;
     try {
-      fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class,
-          FileSymlinkException.class, InconsistentFilesystemException.class);
-    } catch (IOException e) {
-      throw new ASTLookupFunctionException(new ErrorReadingSkylarkExtensionException(e),
-          Transience.PERSISTENT);
-    } catch (FileSymlinkException e) {
-      throw new ASTLookupFunctionException(new ErrorReadingSkylarkExtensionException(e),
-          Transience.PERSISTENT);
+      fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class);
     } catch (InconsistentFilesystemException e) {
       throw new ASTLookupFunctionException(e, Transience.PERSISTENT);
+    } catch (IOException e) {
+      throw new ASTLookupFunctionException(
+          new ErrorReadingSkylarkExtensionException(e), Transience.PERSISTENT);
     }
     if (fileValue == null) {
       return null;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
index a525bf2..d060835 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
@@ -575,17 +575,13 @@
     }
     RootedPath realRootedPath = RootedPath.toRootedPathMaybeUnderRoot(realPath,
         ImmutableList.of(artifact.getRoot().getPath()));
-    FileStateValue fileStateValue;
-    FileStateValue realFileStateValue;
-    try {
-      fileStateValue = FileStateValue.createWithStatNoFollow(rootedPath, statNoFollow, tsgm);
-      // TODO(bazel-team): consider avoiding a 'stat' here when the symlink target hasn't changed
-      // and is a source file (since changes to those are checked separately).
-      realFileStateValue = realPath.equals(path) ? fileStateValue
-          : FileStateValue.create(realRootedPath, tsgm);
-    } catch (InconsistentFilesystemException e) {
-      throw new IOException(e);
-    }
+    FileStateValue fileStateValue =
+        FileStateValue.createWithStatNoFollow(rootedPath, statNoFollow, tsgm);
+    // TODO(bazel-team): consider avoiding a 'stat' here when the symlink target hasn't changed
+    // and is a source file (since changes to those are checked separately).
+    FileStateValue realFileStateValue = realPath.equals(path)
+        ? fileStateValue
+        : FileStateValue.create(realRootedPath, tsgm);
     return FileValue.value(rootedPath, fileStateValue, realRootedPath, realFileStateValue);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java
index 10016a1..e607b19 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java
@@ -218,9 +218,8 @@
         artifact.getPath()));
     FileValue fileValue;
     try {
-      fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class,
-          InconsistentFilesystemException.class, FileSymlinkException.class);
-    } catch (IOException | InconsistentFilesystemException | FileSymlinkException e) {
+      fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class);
+    } catch (IOException e) {
       throw makeMissingInputFileException(artifact, mandatory, e, env.getListener());
     }
     if (fileValue == null) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/DirtinessCheckerUtils.java b/src/main/java/com/google/devtools/build/lib/skyframe/DirtinessCheckerUtils.java
index 5400bf9..eae3b0c 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/DirtinessCheckerUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/DirtinessCheckerUtils.java
@@ -46,7 +46,7 @@
       RootedPath rootedPath = (RootedPath) key.argument();
       try {
         return FileStateValue.create(rootedPath, tsgm);
-      } catch (InconsistentFilesystemException | IOException e) {
+      } catch (IOException e) {
         // TODO(bazel-team): An IOException indicates a failure to get a file digest or a symlink
         // target, not a missing file. Such a failure really shouldn't happen, so failing early
         // may be better here.
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ErrorReadingSkylarkExtensionException.java b/src/main/java/com/google/devtools/build/lib/skyframe/ErrorReadingSkylarkExtensionException.java
index cf6904a..f459aea 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ErrorReadingSkylarkExtensionException.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ErrorReadingSkylarkExtensionException.java
@@ -25,8 +25,4 @@
   public ErrorReadingSkylarkExtensionException(IOException e) {
     super(e.getMessage(), e);
   }
-
-  public ErrorReadingSkylarkExtensionException(FileSymlinkException e) {
-    super(e.getMessage(), e);
-  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java
index 853ab88..74980d2 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java
@@ -252,7 +252,8 @@
         // reported exactly once.
         return null;
       }
-      throw new FileFunctionException(Preconditions.checkNotNull(fse, rootedPath));
+      throw new FileFunctionException(
+          Preconditions.checkNotNull(fse, rootedPath), Transience.PERSISTENT);
     }
 
     return resolveFromAncestors(symlinkTargetRootedPath, env);
@@ -278,15 +279,6 @@
    * {@link FileFunction#compute}.
    */
   private static final class FileFunctionException extends SkyFunctionException {
-
-    public FileFunctionException(InconsistentFilesystemException e, Transience transience) {
-      super(e, transience);
-    }
-
-    public FileFunctionException(FileSymlinkException e) {
-      super(e, Transience.PERSISTENT);
-    }
-
     public FileFunctionException(IOException e, Transience transience) {
       super(e, transience);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileStateFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileStateFunction.java
index dece88c..52876f2 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FileStateFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileStateFunction.java
@@ -54,8 +54,6 @@
       return FileStateValue.NONEXISTENT_FILE_STATE_NODE;
     } catch (IOException e) {
       throw new FileStateFunctionException(e);
-    } catch (InconsistentFilesystemException e) {
-      throw new FileStateFunctionException(e);
     }
   }
 
@@ -72,9 +70,5 @@
     public FileStateFunctionException(IOException e) {
       super(e, Transience.TRANSIENT);
     }
-
-    public FileStateFunctionException(InconsistentFilesystemException e) {
-      super(e, Transience.TRANSIENT);
-    }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileSymlinkException.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileSymlinkException.java
index c2b00cf..19ecace 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FileSymlinkException.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileSymlinkException.java
@@ -13,8 +13,10 @@
 // limitations under the License.
 package com.google.devtools.build.lib.skyframe;
 
+import java.io.IOException;
+
 /** Exception indicating a problem with symlinks. */
-public abstract class FileSymlinkException extends Exception {
+public abstract class FileSymlinkException extends IOException {
   protected FileSymlinkException(String message) {
     super(message);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/InconsistentFilesystemException.java b/src/main/java/com/google/devtools/build/lib/skyframe/InconsistentFilesystemException.java
index 318aa2d..5397fb4 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/InconsistentFilesystemException.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/InconsistentFilesystemException.java
@@ -13,11 +13,13 @@
 // limitations under the License.
 package com.google.devtools.build.lib.skyframe;
 
+import java.io.IOException;
+
 /**
  * Used to indicate a filesystem inconsistency, e.g. file 'a/b' exists but directory 'a' doesn't
  * exist. This generally means the result of the build is undefined but we shouldn't crash hard.
  */
-public class InconsistentFilesystemException extends Exception {
+public class InconsistentFilesystemException extends IOException {
   public InconsistentFilesystemException(String inconsistencyMessage) {
     super("Inconsistent filesystem operations. " + inconsistencyMessage + " The results of the "
         + "build are not guaranteed to be correct. You should probably run 'blaze clean' and "
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunction.java
index d1ffe0e..66d4afa 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunction.java
@@ -98,12 +98,7 @@
                   .getRelativePath()
                   .getRelative(BuildFileName.WORKSPACE.getFilenameFragment()));
       FileValue workspaceFileValue =
-          (FileValue)
-              env.getValueOrThrow(
-                  FileValue.key(workspaceRootedFile),
-                  IOException.class,
-                  FileSymlinkException.class,
-                  InconsistentFilesystemException.class);
+          (FileValue) env.getValueOrThrow(FileValue.key(workspaceRootedFile), IOException.class);
       if (workspaceFileValue == null) {
         return Optional.absent();
       }
@@ -112,10 +107,10 @@
         return Optional.of(false);
       }
       return Optional.of(workspaceFileValue.exists());
-    } catch (IOException e) {
+    } catch (InconsistentFilesystemException e) {
       throw new LocalRepositoryLookupFunctionException(
           new ErrorDeterminingRepositoryException(
-              "IOException while checking if there is a WORKSPACE file in "
+              "InconsistentFilesystemException while checking if there is a WORKSPACE file in "
                   + directory.asPath().getPathString(),
               e),
           Transience.PERSISTENT);
@@ -126,10 +121,10 @@
                   + directory.asPath().getPathString(),
               e),
           Transience.PERSISTENT);
-    } catch (InconsistentFilesystemException e) {
+    } catch (IOException e) {
       throw new LocalRepositoryLookupFunctionException(
           new ErrorDeterminingRepositoryException(
-              "InconsistentFilesystemException while checking if there is a WORKSPACE file in "
+              "IOException while checking if there is a WORKSPACE file in "
                   + directory.asPath().getPathString(),
               e),
           Transience.PERSISTENT);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
index c3e67c7..b10ab4b 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
@@ -66,9 +66,9 @@
 import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.SkyValue;
+import com.google.devtools.build.skyframe.ValueOrException;
 import com.google.devtools.build.skyframe.ValueOrException2;
 import com.google.devtools.build.skyframe.ValueOrException3;
-import com.google.devtools.build.skyframe.ValueOrException4;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -296,18 +296,17 @@
     Preconditions.checkState(
         Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.FILE)), depKeys);
     boolean packageShouldBeInError = packageWasInError;
-    for (Map.Entry<SkyKey, ValueOrException3<IOException, FileSymlinkException,
-        InconsistentFilesystemException>> entry : env.getValuesOrThrow(depKeys, IOException.class,
-            FileSymlinkException.class, InconsistentFilesystemException.class).entrySet()) {
+    for (Map.Entry<SkyKey, ValueOrException<IOException>> entry :
+        env.getValuesOrThrow(depKeys, IOException.class).entrySet()) {
       try {
         entry.getValue().get();
-      } catch (IOException e) {
-        maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError);
+      } catch (InconsistentFilesystemException e) {
+        throw new InternalInconsistentFilesystemException(packageIdentifier, e);
       } catch (FileSymlinkException e) {
         // Legacy doesn't detect symlink cycles.
         packageShouldBeInError = true;
-      } catch (InconsistentFilesystemException e) {
-        throw new InternalInconsistentFilesystemException(packageIdentifier, e);
+      } catch (IOException e) {
+        maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError);
       }
     }
     return packageShouldBeInError;
@@ -326,19 +325,18 @@
     Preconditions.checkState(
         Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.GLOB)), depKeys);
     boolean packageShouldBeInError = packageWasInError;
-    for (Map.Entry<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
-        FileSymlinkException, InconsistentFilesystemException>> entry :
-        env.getValuesOrThrow(depKeys, IOException.class, BuildFileNotFoundException.class,
-            FileSymlinkException.class, InconsistentFilesystemException.class).entrySet()) {
+    for (Map.Entry<SkyKey, ValueOrException2<IOException, BuildFileNotFoundException>> entry :
+        env.getValuesOrThrow(
+            depKeys, IOException.class, BuildFileNotFoundException.class).entrySet()) {
       try {
         entry.getValue().get();
-      } catch (IOException | BuildFileNotFoundException e) {
-        maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError);
+      } catch (InconsistentFilesystemException e) {
+        throw new InternalInconsistentFilesystemException(packageIdentifier, e);
       } catch (FileSymlinkException e) {
         // Legacy doesn't detect symlink cycles.
         packageShouldBeInError = true;
-      } catch (InconsistentFilesystemException e) {
-        throw new InternalInconsistentFilesystemException(packageIdentifier, e);
+      } catch (IOException | BuildFileNotFoundException e) {
+        maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError);
       }
     }
     return packageShouldBeInError;
@@ -453,12 +451,9 @@
               env.getValueOrThrow(
                   workspaceKey,
                   IOException.class,
-                  FileSymlinkException.class,
-                  InconsistentFilesystemException.class,
                   EvalException.class,
                   SkylarkImportFailedException.class);
-    } catch (IOException | FileSymlinkException | InconsistentFilesystemException
-          | EvalException | SkylarkImportFailedException e) {
+    } catch (IOException | EvalException | SkylarkImportFailedException e) {
       throw new PackageFunctionException(
           new NoSuchPackageException(
               Label.EXTERNAL_PACKAGE_IDENTIFIER,
@@ -654,10 +649,9 @@
       throws InterruptedException {
     FileValue buildFileValue;
     try {
-      buildFileValue = (FileValue) env.getValueOrThrow(FileValue.key(buildFileRootedPath),
-          IOException.class, FileSymlinkException.class,
-          InconsistentFilesystemException.class);
-    } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
+      buildFileValue =
+          (FileValue) env.getValueOrThrow(FileValue.key(buildFileRootedPath), IOException.class);
+    } catch (IOException e) {
       throw new IllegalStateException("Package lookup succeeded but encountered error when "
           + "getting FileValue for BUILD file directly.", e);
     }
@@ -1051,10 +1045,10 @@
       }
       globDepsRequested.addAll(globKeys);
 
-      Map<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
-          FileSymlinkCycleException, InconsistentFilesystemException>> globValueMap =
+      Map<SkyKey, ValueOrException3<IOException, BuildFileNotFoundException,
+          FileSymlinkCycleException>> globValueMap =
           env.getValuesOrThrow(globKeys, IOException.class, BuildFileNotFoundException.class,
-              FileSymlinkCycleException.class, InconsistentFilesystemException.class);
+              FileSymlinkCycleException.class);
 
       // For each missing glob, evaluate it asychronously via the delegate.
       //
@@ -1087,12 +1081,12 @@
     }
 
     private Collection<SkyKey> getMissingKeys(Collection<SkyKey> globKeys,
-        Map<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
-            FileSymlinkCycleException, InconsistentFilesystemException>> globValueMap) {
+        Map<SkyKey, ValueOrException3<IOException, BuildFileNotFoundException,
+            FileSymlinkCycleException>> globValueMap) {
       List<SkyKey> missingKeys = new ArrayList<>(globKeys.size());
       for (SkyKey globKey : globKeys) {
-        ValueOrException4<IOException, BuildFileNotFoundException, FileSymlinkCycleException,
-            InconsistentFilesystemException> valueOrException = globValueMap.get(globKey);
+        ValueOrException3<IOException, BuildFileNotFoundException, FileSymlinkCycleException>
+            valueOrException = globValueMap.get(globKey);
         if (valueOrException == null) {
           missingKeys.add(globKey);
         }
@@ -1100,8 +1094,7 @@
           if (valueOrException.get() == null) {
             missingKeys.add(globKey);
           }
-        } catch (IOException | BuildFileNotFoundException | FileSymlinkCycleException
-            | InconsistentFilesystemException doesntMatter) {
+        } catch (IOException | BuildFileNotFoundException doesntMatter) {
           continue;
         }
       }
@@ -1156,8 +1149,8 @@
      */
     private static class HybridToken extends Globber.Token {
       // The result of the Skyframe lookup for all the needed glob patterns.
-      private final Map<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
-          FileSymlinkCycleException, InconsistentFilesystemException>> globValueMap;
+      private final Map<SkyKey, ValueOrException3<IOException, BuildFileNotFoundException,
+          FileSymlinkCycleException>> globValueMap;
       // The skyframe keys corresponding to the 'includes' patterns fetched from Skyframe
       // (this is includes_sky above).
       private final Iterable<SkyKey> includesGlobKeys;
@@ -1169,8 +1162,8 @@
       // A token for computing excludes_leg.
       private final Token legacyExcludesToken;
 
-      private HybridToken(Map<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
-          FileSymlinkCycleException, InconsistentFilesystemException>> globValueMap,
+      private HybridToken(Map<SkyKey, ValueOrException3<IOException, BuildFileNotFoundException,
+          FileSymlinkCycleException>> globValueMap,
           Iterable<SkyKey> includesGlobKeys, Iterable<SkyKey> excludesGlobKeys,
           Token delegateIncludesToken, Token delegateExcludesToken) {
         this.globValueMap = globValueMap;
@@ -1208,20 +1201,18 @@
           SkyKey globKey,
           Map<
                   SkyKey,
-                  ValueOrException4<
-                      IOException, BuildFileNotFoundException, FileSymlinkCycleException,
-                      InconsistentFilesystemException>>
+                  ValueOrException3<
+                      IOException, BuildFileNotFoundException, FileSymlinkCycleException>>
               globValueMap)
           throws IOException {
-        ValueOrException4<IOException, BuildFileNotFoundException, FileSymlinkCycleException,
-            InconsistentFilesystemException> valueOrException =
+        ValueOrException3<IOException, BuildFileNotFoundException, FileSymlinkCycleException>
+            valueOrException =
                 Preconditions.checkNotNull(globValueMap.get(globKey), "%s should not be missing",
                     globKey);
         try {
           return Preconditions.checkNotNull((GlobValue) valueOrException.get(),
               "%s should not be missing", globKey).getMatches();
-        } catch (BuildFileNotFoundException | FileSymlinkCycleException
-            | InconsistentFilesystemException e) {
+        } catch (BuildFileNotFoundException e) {
           // Legacy package loading is only able to handle an IOException, so a rethrow here is the
           // best we can do. But after legacy package loading, PackageFunction will go through all
           // the skyframe deps and properly handle InconsistentFilesystemExceptions.
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java
index 6c29df3..00d0c34 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java
@@ -146,8 +146,15 @@
     SkyKey fileSkyKey = FileValue.key(fileRootedPath);
     FileValue fileValue = null;
     try {
-      fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class,
-          FileSymlinkException.class, InconsistentFilesystemException.class);
+      fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class);
+    } catch (InconsistentFilesystemException e) {
+      // This error is not transient from the perspective of the PackageLookupFunction.
+      throw new PackageLookupFunctionException(e, Transience.PERSISTENT);
+    } catch (FileSymlinkException e) {
+      throw new PackageLookupFunctionException(new BuildFileNotFoundException(packageIdentifier,
+          "Symlink cycle detected while trying to find " + basename + " file "
+              + fileRootedPath.asPath()),
+          Transience.PERSISTENT);
     } catch (IOException e) {
       // TODO(bazel-team): throw an IOException here and let PackageFunction wrap that into a
       // BuildFileNotFoundException.
@@ -155,14 +162,6 @@
           "IO errors while looking for " + basename + " file reading "
               + fileRootedPath.asPath() + ": " + e.getMessage(), e),
           Transience.PERSISTENT);
-    } catch (FileSymlinkException e) {
-      throw new PackageLookupFunctionException(new BuildFileNotFoundException(packageIdentifier,
-          "Symlink cycle detected while trying to find " + basename + " file "
-              + fileRootedPath.asPath()),
-          Transience.PERSISTENT);
-    } catch (InconsistentFilesystemException e) {
-      // This error is not transient from the perspective of the PackageLookupFunction.
-      throw new PackageLookupFunctionException(e, Transience.PERSISTENT);
     }
     return fileValue;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ProcessPackageDirectory.java b/src/main/java/com/google/devtools/build/lib/skyframe/ProcessPackageDirectory.java
index 7418505..e46f4f2 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ProcessPackageDirectory.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ProcessPackageDirectory.java
@@ -30,7 +30,7 @@
 import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyKey;
-import com.google.devtools.build.skyframe.ValueOrException4;
+import com.google.devtools.build.skyframe.ValueOrException2;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -78,14 +78,8 @@
     SkyKey fileKey = FileValue.key(rootedPath);
     FileValue fileValue;
     try {
-      fileValue =
-          (FileValue)
-              env.getValueOrThrow(
-                  fileKey,
-                  InconsistentFilesystemException.class,
-                  FileSymlinkException.class,
-                  IOException.class);
-    } catch (InconsistentFilesystemException | FileSymlinkException | IOException e) {
+      fileValue = (FileValue) env.getValueOrThrow(fileKey, IOException.class);
+    } catch (IOException e) {
       return reportErrorAndReturn(
           "Failed to get information about path", e, rootRelativePath, env.getListener());
     }
@@ -116,15 +110,12 @@
     SkyKey dirListingKey = DirectoryListingValue.key(rootedPath);
     Map<
             SkyKey,
-            ValueOrException4<
-                NoSuchPackageException, InconsistentFilesystemException, FileSymlinkException,
-                IOException>>
+            ValueOrException2<
+                NoSuchPackageException, IOException>>
         pkgLookupAndDirectoryListingDeps =
             env.getValuesOrThrow(
                 ImmutableList.of(pkgLookupKey, dirListingKey),
                 NoSuchPackageException.class,
-                InconsistentFilesystemException.class,
-                FileSymlinkException.class,
                 IOException.class);
     if (env.valuesMissing()) {
       return null;
@@ -141,7 +132,7 @@
                   pkgLookupKey);
     } catch (NoSuchPackageException | InconsistentFilesystemException e) {
       return reportErrorAndReturn("Failed to load package", e, rootRelativePath, env.getListener());
-    } catch (IOException | FileSymlinkException e) {
+    } catch (IOException e) {
       throw new IllegalStateException(e);
     }
     DirectoryListingValue dirListingValue;
@@ -154,15 +145,15 @@
                   rootedPath,
                   repositoryName,
                   dirListingKey);
-    } catch (InconsistentFilesystemException | IOException e) {
-      return reportErrorAndReturn(
-          "Failed to list directory contents", e, rootRelativePath, env.getListener());
     } catch (FileSymlinkException e) {
       // DirectoryListingFunction only throws FileSymlinkCycleException when FileFunction throws it,
       // but FileFunction was evaluated for rootedPath above, and didn't throw there. It shouldn't
       // be able to avoid throwing there but throw here.
       throw new IllegalStateException(
           "Symlink cycle found after not being found for \"" + rootedPath + "\"");
+    } catch (IOException e) {
+      return reportErrorAndReturn(
+          "Failed to list directory contents", e, rootRelativePath, env.getListener());
     } catch (NoSuchPackageException e) {
       throw new IllegalStateException(e);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
index 0706308..5b0549f 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
@@ -174,7 +174,7 @@
       // We are free to traverse this directory.
       Collection<SkyKey> dependentKeys = createRecursiveTraversalKeys(env, traversal);
       return resultForDirectory(traversal, rootInfo, traverseChildren(env, dependentKeys));
-    } catch (FileSymlinkException | InconsistentFilesystemException | IOException e) {
+    } catch (IOException e) {
       throw new RecursiveFilesystemTraversalFunctionException(
           new FileOperationException("Error while traversing fileset: " + e.getMessage()));
     } catch (MissingDepException e) {
@@ -213,16 +213,10 @@
   }
 
   private static FileInfo lookUpFileInfo(Environment env, TraversalRequest traversal)
-      throws MissingDepException, FileSymlinkException, InconsistentFilesystemException,
-          IOException, InterruptedException {
+      throws MissingDepException, IOException, InterruptedException {
     // Stat the file.
     FileValue fileValue =
-        (FileValue)
-            env.getValueOrThrow(
-                FileValue.key(traversal.path),
-                FileSymlinkException.class,
-                InconsistentFilesystemException.class,
-                IOException.class);
+        (FileValue) env.getValueOrThrow(FileValue.key(traversal.path), IOException.class);
 
     if (env.valuesMissing()) {
       throw new MissingDepException();
@@ -301,8 +295,7 @@
    */
   private static PkgLookupResult checkIfPackage(
       Environment env, TraversalRequest traversal, FileInfo rootInfo)
-      throws MissingDepException, FileSymlinkException, InconsistentFilesystemException,
-          IOException, InterruptedException {
+      throws MissingDepException, IOException, InterruptedException {
     Preconditions.checkArgument(rootInfo.type.exists() && !rootInfo.type.isFile(),
         "{%s} {%s}", traversal, rootInfo);
     PackageLookupValue pkgLookup = (PackageLookupValue) getDependentSkyValue(env,