diff --git a/src/filesystem/Filesystem.php b/src/filesystem/Filesystem.php
--- a/src/filesystem/Filesystem.php
+++ b/src/filesystem/Filesystem.php
@@ -1103,12 +1103,24 @@
         }
         return null;
       }
-      $stdout = head($stdout);
+
+      // These are the only file extensions that can be executed directly
+      // when using proc_open() with 'bypass_shell'.
+      $executable_extensions = ['.exe', '.bat', '.cmd', '.com'];
+
+      foreach ($stdout as $line) {
+        $path = trim($line);
+        foreach ($executable_extensions as $ext) {
+          if (substr($path, -strlen($ext)) === $ext) {
+            return $path;
+          }
+        }
+      }
+      return null;
     } else {
       list($err, $stdout) = exec_manual('which %s', $binary);
+      return $err === 0 ? trim($stdout) : null;
     }
-
-    return $err === 0 ? trim($stdout) : null;
   }
 
 
diff --git a/src/lint/linter/ArcanistExternalLinter.php b/src/lint/linter/ArcanistExternalLinter.php
--- a/src/lint/linter/ArcanistExternalLinter.php
+++ b/src/lint/linter/ArcanistExternalLinter.php
@@ -133,7 +133,19 @@
    * @task bin
    */
   final public function getBinary() {
-    return coalesce($this->bin, $this->getDefaultBinary());
+    $bin = coalesce($this->bin, $this->getDefaultBinary());
+    if (phutil_is_windows()) {
+      // On Windows, we use proc_open with 'bypass_shell' option, which will
+      // resolve %PATH%, but not %PATHEXT% (unless the extension is .exe).
+      // Therefore find the right binary ourselves.
+      $resolved = Filesystem::resolveBinary($bin);
+      if ($resolved) {
+        // If we can't find it, leave it unresolved, as this string will be
+        // used in some error messages elsewhere.
+        $bin = $resolved;
+      }
+    }
+    return $bin;
   }
 
   /**