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
@@ -339,7 +339,7 @@
       $path_argument = $this->getPathArgumentForLinterFuture($disk_path);
       $future = new ExecFuture('%C %C', $bin, $path_argument);
 
-      $future->setCWD($this->getEngine()->getWorkingCopy()->getProjectRoot());
+      $future->setCWD($this->getProjectRoot());
       $futures[$path] = $future;
     }
 
@@ -407,8 +407,7 @@
   public function setLinterConfigurationValue($key, $value) {
     switch ($key) {
       case 'interpreter':
-        $working_copy = $this->getEngine()->getWorkingCopy();
-        $root = $working_copy->getProjectRoot();
+        $root = $this->getProjectRoot();
 
         foreach ((array)$value as $path) {
           if (Filesystem::binaryExists($path)) {
@@ -429,8 +428,7 @@
       case 'bin':
         $is_script = $this->shouldUseInterpreter();
 
-        $working_copy = $this->getEngine()->getWorkingCopy();
-        $root = $working_copy->getProjectRoot();
+        $root = $this->getProjectRoot();
 
         foreach ((array)$value as $path) {
           if (!$is_script && Filesystem::binaryExists($path)) {
diff --git a/src/lint/linter/ArcanistLinter.php b/src/lint/linter/ArcanistLinter.php
--- a/src/lint/linter/ArcanistLinter.php
+++ b/src/lint/linter/ArcanistLinter.php
@@ -248,6 +248,24 @@
     return $this;
   }
 
+  final public function getProjectRoot() {
+    $engine = $this->getEngine();
+    if (!$engine) {
+      throw new Exception(
+        pht(
+          'You must call %s before you can call %s.',
+          'setEngine()',
+          __FUNCTION__.'()'));
+    }
+
+    $working_copy = $engine->getWorkingCopy();
+    if (!$working_copy) {
+      return null;
+    }
+
+    return $working_copy->getProjectRoot();
+  }
+
   final public function getOtherLocation($offset, $path = null) {
     if ($path === null) {
       $path = $this->getActivePath();
@@ -386,7 +404,7 @@
   }
 
   final protected function addLintMessage(ArcanistLintMessage $message) {
-    $root = $this->getEngine()->getWorkingCopy()->getProjectRoot();
+    $root = $this->getProjectRoot();
     $path = Filesystem::resolvePath($message->getPath(), $root);
     $message->setPath(Filesystem::readablePath($path, $root));
 
diff --git a/src/lint/linter/ArcanistScriptAndRegexLinter.php b/src/lint/linter/ArcanistScriptAndRegexLinter.php
--- a/src/lint/linter/ArcanistScriptAndRegexLinter.php
+++ b/src/lint/linter/ArcanistScriptAndRegexLinter.php
@@ -180,7 +180,7 @@
    */
   public function willLintPaths(array $paths) {
     $script = $this->getConfiguredScript();
-    $root   = $this->getEngine()->getWorkingCopy()->getProjectRoot();
+    $root   = $this->getProjectRoot();
 
     $futures = array();
     foreach ($paths as $path) {
diff --git a/src/lint/linter/ArcanistSpellingLinter.php b/src/lint/linter/ArcanistSpellingLinter.php
--- a/src/lint/linter/ArcanistSpellingLinter.php
+++ b/src/lint/linter/ArcanistSpellingLinter.php
@@ -54,7 +54,7 @@
   }
 
   public function loadDictionary($path) {
-    $root = $this->getEngine()->getWorkingCopy()->getProjectRoot();
+    $root = $this->getProjectRoot();
     $path = Filesystem::resolvePath($path, $root);
 
     $dict = phutil_json_decode(Filesystem::readFile($path));