Repository: zizmorcore/zizmor (Rust)

Issue:
[BUG]: zizmor Crash Report (main branch)
### Pre-submission checks

- [x] I am **not** filing a feature request. These should be filed via the feature request form instead.
- [x] I have checked the [Troubleshooting Guide](https://docs.zizmor.sh/troubleshooting) for my problem.
- [x] I have looked through both the [open](https://github.com/zizmorcore/zizmor/issues?q=is%3Aissue+is%3Aopen+) and [closed](https://github.com/zizmorcore/zizmor/issues?q=is%3Aissue%20state%3Aclosed) issues for a duplicate report.

### zizmor version

zizmor 1.16.3

### Expected behavior

Ran `zizmor --lsp` from the main branch (I was attempting to do some hacking on it)

The problem does not happen with any released version.


### Actual behavior

Git bisect says:

2e85156ddfaead0563b49fa682d97194d7a51be7 is the first bad commit
commit 2e85156ddfaead0563b49fa682d97194d7a51be7
Author: William Woodruff <william@yossarian.net>
Date:   Fri Nov 7 19:04:53 2025 -0500

    feat: make zizmor's core async (#1314)

### Reproduction steps

1. cargo build
2. zizmor --lsp

### Logs

```plain text
$ target/release/zizmor --lsp
Well, this is embarrassing.

zizmor had a problem and crashed. To help us diagnose the problem you can send us a crash report.

We have generated a report file at "/tmp/report-69de5ef6-554e-43cb-bea4-91a87de28c63.toml". Submit an issue or email with the subject of "zizmor Crash Report" and include the report as an attachment.

- Homepage: https://docs.zizmor.sh
- Authors: William Woodruff <william@yossarian.net>

We take privacy seriously, and do not perform any automated error collection. In order to improve the software, we rely on people to submit reports.

Thank you kindly!
```

### Additional context

From the crash report:

```toml
name = "zizmor"
operating_system = "Arch Linux [64-bit]"
crate_version = "1.16.3"
explanation = """
Panic occurred in file '/home/rmuir/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.48.0/src/runtime/scheduler/multi_thread/mod.rs' at line 86
"""
cause = "Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks."
method = "Panic"
backtrace = """
   0:     0x555555d2a5ee - human_panic::report::Report::new::hec0053b0486fd151
   1:     0x555556450ccb - human_panic::setup_panic::{{closure}}::h6cb9fc10e79f9d1e
   2:     0x5555560abac6 - std::panicking::panic_with_hook::h011a155645fbd86e
   3:     0x5555560d5d35 - std::panicking::panic_handler::{{closure}}::h55a4680715609b11
   4:     0x5555560d5cc9 - std::sys::backtrace::__rust_end_short_backtrace::h5a0c9848a87e1635
   5:     0x5555560d5cbc - __rustc[1c8c0b0c56d5ad47]::rust_begin_unwind
   6:     0x555555c2af1f - core::panicking::panic_fmt::ha9e78b3b184ec3c5
   7:     0x555555afa3a0 - zizmor::lsp::run::hb5b00a67203b5959
   8:     0x555555afaac2 - zizmor::run::{{closure}}::h748c432a736f19bd
   9:     0x5555562db34b - zizmor::main::h8d8683dccf85cd92
  10:     0x555556576213 - std::sys::backtrace::__rust_begin_short_backtrace::h5a9e71c9b3a7a2a0
  11:     0x555556318ee1 - main
  12:     0x7ffff7c27635 - <unresolved>
  13:     0x7ffff7c276e9 - __libc_start_main
  14:     0x555555ac2625 - _start
  15:                0x0 - <unresolved>
"""
```


Discussion:
I fixed it locally like this, but I don't speak rust...
```diff
diff --git a/crates/zizmor/src/lsp.rs b/crates/zizmor/src/lsp.rs
index 8a821b2..46350dc 100644
--- a/crates/zizmor/src/lsp.rs
+++ b/crates/zizmor/src/lsp.rs
@@ -275,7 +275,6 @@ impl From<Point> for lsp_types::Position {
     }
 }
 
-#[tokio::main]
 pub(crate) async fn run() -> Result<(), Error> {
     let stdin = tokio::io::stdin();
     let stdout = tokio::io::stdout();
diff --git a/crates/zizmor/src/main.rs b/crates/zizmor/src/main.rs
index 9c8806f..d5d648d 100644
--- a/crates/zizmor/src/main.rs
+++ b/crates/zizmor/src/main.rs
@@ -580,7 +580,7 @@ enum Error {
 async fn run(app: &mut App) -> Result<ExitCode, Error> {
     #[cfg(feature = "lsp")]
     if app.lsp.lsp {
-        lsp::run()?;
+        let _ = lsp::run().await;
         return Ok(ExitCode::SUCCESS);
     }
```
Hey @rmuir, thanks for the report!

Yeah, this is an artifact of the async refactor -- I just forgot to remove the `[tokio::main]` from that LSP startup function. I'll have a fix up for that in a second.

(Thanks a ton for raising this -- this is a good example of a weak spot in the current integration tests.)
(Your patch is like 99% correct -- the only thing that needs changing is removing the `let _`, since that'll drop any errors from the LSP mode on the floor rather than propagating them 🙂)
Fixed with #1356. Thanks again!



Patch:
diff --git a/crates/zizmor/src/lsp.rs b/crates/zizmor/src/lsp.rs
index 8a821b2a7..46350dc90 100644
--- a/crates/zizmor/src/lsp.rs
+++ b/crates/zizmor/src/lsp.rs
@@ -275,7 +275,6 @@ impl From<Point> for lsp_types::Position {
     }
 }
 
-#[tokio::main]
 pub(crate) async fn run() -> Result<(), Error> {
     let stdin = tokio::io::stdin();
     let stdout = tokio::io::stdout();
diff --git a/crates/zizmor/src/main.rs b/crates/zizmor/src/main.rs
index 9c8806fd1..24d7e3bcc 100644
--- a/crates/zizmor/src/main.rs
+++ b/crates/zizmor/src/main.rs
@@ -580,7 +580,7 @@ enum Error {
 async fn run(app: &mut App) -> Result<ExitCode, Error> {
     #[cfg(feature = "lsp")]
     if app.lsp.lsp {
-        lsp::run()?;
+        lsp::run().await?;
         return Ok(ExitCode::SUCCESS);
     }
 


Test patch:
diff --git a/crates/zizmor/tests/integration/common.rs b/crates/zizmor/tests/integration/common.rs
index 4051ce383..26b8b7ad6 100644
--- a/crates/zizmor/tests/integration/common.rs
+++ b/crates/zizmor/tests/integration/common.rs
@@ -39,6 +39,7 @@ pub enum OutputMode {
 
 pub struct Zizmor {
     cmd: Command,
+    stdin: Option<String>,
     unbuffer: bool,
     offline: bool,
     inputs: Vec<String>,
@@ -55,6 +56,7 @@ impl Zizmor {
 
         Self {
             cmd,
+            stdin: None,
             unbuffer: false,
             offline: true,
             inputs: vec![],
@@ -65,6 +67,11 @@ impl Zizmor {
         }
     }
 
+    pub fn stdin(mut self, input: impl Into<String>) -> Self {
+        self.stdin = Some(input.into());
+        self
+    }
+
     pub fn args<'a>(mut self, args: impl IntoIterator<Item = &'a str>) -> Self {
         self.cmd.args(args);
         self
@@ -124,6 +131,10 @@ impl Zizmor {
     }
 
     pub fn run(mut self) -> Result<String> {
+        if let Some(stdin) = &self.stdin {
+            self.cmd.write_stdin(stdin.as_bytes());
+        }
+
         if self.offline {
             self.cmd.arg("--offline");
         } else {
diff --git a/crates/zizmor/tests/integration/e2e.rs b/crates/zizmor/tests/integration/e2e.rs
index afc6aeee3..d29ac971f 100644
--- a/crates/zizmor/tests/integration/e2e.rs
+++ b/crates/zizmor/tests/integration/e2e.rs
@@ -448,3 +448,26 @@ fn issue_1341() -> Result<()> {
 
     Ok(())
 }
+
+/// Regression test for #1356.
+///
+/// Ensures that zizmor's LSP mode (`--lsp`) starts up correctly, i.e.
+/// doesn't crash on launch.
+#[test]
+fn issue_1356_lsp_mode_starts() -> Result<()> {
+    insta::assert_snapshot!(
+        zizmor()
+            .expects_failure(false)
+            .output(OutputMode::Stdout)
+            .stdin("{}") // Not a valid LSP message, but all we're testing is startup.
+            .args(["--lsp"])
+            .run()?,
+        @r#"
+    Content-Length: 75
+
+    {"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}
+    "#
+    );
+
+    Ok(())
+}