diff --git a/lib/cc/cli/runner.rb b/lib/cc/cli/runner.rb
index <HASH>..<HASH> 100644
--- a/lib/cc/cli/runner.rb
+++ b/lib/cc/cli/runner.rb
@@ -11,6 +11,7 @@ module CC
         $stderr.puts("error: (#{ex.class}) #{ex.message}")
 
         CLI.debug("backtrace: #{ex.backtrace.join("\n\t")}")
+        exit 1
       end
 
       def initialize(args)
diff --git a/spec/cc/cli/runner_spec.rb b/spec/cc/cli/runner_spec.rb
index <HASH>..<HASH> 100644
--- a/spec/cc/cli/runner_spec.rb
+++ b/spec/cc/cli/runner_spec.rb
@@ -30,11 +30,12 @@ module CC::CLI
           end
         end
 
-        _, stderr = capture_io do
+        _, stderr, exit_code = capture_io_and_exit_code do
           Runner.run(["explode"])
         end
 
         expect(stderr).to match(/error: \(StandardError\) boom/)
+        expect(exit_code).to eq(1)
       end
 
       it "doesn't check for new version when --no-check-version is passed" do
Exit 1 when due to an unexpected error

We exit 0 in `Runner` when handling exceptions. That's pretty surprising, and is
very problematic for any attempt to use the CLI within a script or an editor
plugin or anything of that kind: it can lead to accidentally think everything
went fine & found no issues when in fact analysis just didn't run. I'm shocked
we've made it this long without this being a problem, actually.