diff --git a/src/filesystem/Filesystem.php b/src/filesystem/Filesystem.php
--- a/src/filesystem/Filesystem.php
+++ b/src/filesystem/Filesystem.php
@@ -1118,11 +1118,46 @@
    * @task   assert
    */
   public static function assertExists($path) {
-    if (!self::pathExists($path)) {
-      throw new FilesystemException(
-        $path,
-        pht("File system entity '%s' does not exist.", $path));
+    if (self::pathExists($path)) {
+      return;
+    }
+
+    // Before we claim that the path doesn't exist, try to find a parent we
+    // don't have "+x" on. If we find one, tailor the error message so we don't
+    // say "does not exist" in cases where the path does exist, we just don't
+    // have permission to test its existence.
+    foreach (self::walkToRoot($path) as $parent) {
+      if (!self::pathExists($parent)) {
+        continue;
+      }
+
+      if (!is_dir($parent)) {
+        continue;
+      }
+
+      if (phutil_is_windows()) {
+        // Do nothing. On Windows, there's no obvious equivalent to the
+        // check below because "is_executable(...)" always appears to return
+        // "false" for any directory.
+      } else if (!is_executable($parent)) {
+        // On Linux, note that we don't need read permission ("+r") on parent
+        // directories to determine that a path exists, only execute ("+x").
+        throw new FilesystemException(
+          $path,
+          pht(
+            'Filesystem path "%s" can not be accessed because a parent '.
+            'directory ("%s") is not executable (the current process does '.
+            'not have "+x" permission).',
+            $path,
+            $parent));
+      }
     }
+
+    throw new FilesystemException(
+      $path,
+      pht(
+        'Filesystem path "%s" does not exist.',
+        $path));
   }
 
 
diff --git a/src/future/exec/PhutilExecutableFuture.php b/src/future/exec/PhutilExecutableFuture.php
--- a/src/future/exec/PhutilExecutableFuture.php
+++ b/src/future/exec/PhutilExecutableFuture.php
@@ -126,13 +126,51 @@
    * @task config
    */
   final public function setCWD($cwd) {
-    $cwd = (string)$cwd;
+    $cwd = phutil_string_cast($cwd);
+
+    try {
+      Filesystem::assertExists($cwd);
+    } catch (FilesystemException $ex) {
+      throw new PhutilProxyException(
+        pht(
+          'Unable to run a command in directory "%s".',
+          $cwd),
+        $ex);
+    }
 
     if (!is_dir($cwd)) {
       throw new Exception(
         pht(
-          'Preparing to run a command in directory "%s", but that '.
-          'directory does not exist.',
+          'Preparing to run a command in directory "%s", but that path is '.
+          'not a directory.',
+          $cwd));
+    }
+
+    // Although you don't technically need read permission to "chdir()" into
+    // a directory, it is almost certainly a mistake to execute a subprocess
+    // in a CWD we can't read. Refuse to do this. If callers have some
+    // exceptionally clever scheme up their sleeves they can always have the
+    // subprocess "cd" or "chdir()" explicitly.
+
+    if (!is_readable($cwd)) {
+      throw new Exception(
+        pht(
+          'Preparing to run a command in directory "%s", but that directory '.
+          'is not readable (the current process does not have "+r" '.
+          'permission).',
+          $cwd));
+    }
+
+
+    if (phutil_is_windows()) {
+      // Do nothing. On Windows, calling "is_executable(...)" on a directory
+      // always appears to return "false". Skip this check under Windows.
+    } else if (!is_executable($cwd)) {
+      throw new Exception(
+        pht(
+          'Preparing to run a command in directory "%s", but that directory '.
+          'is not executable (the current process does not have "+x" '.
+          'permission).',
           $cwd));
     }