Repository: casey/just (2026-03-26T08:33:21Z)

Issue:
The "#[no-exit-message]" attribute does not silence signal 2 error messages
## just version

```bash
$ just --version            
just 1.42.4
```

## justfile

```justfile
[group('Development')]
[no-exit-message]
@run config="penguin.toml":
    cargo run -- -c {{config}}
```
## running...

```bash
$ just run      
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.34s
     Running `target/debug/core -c penguin.toml`
2025-09-26 18:04:07  INFO start the proxy server and listen on: localhost:9090
^Cerror: Recipe `run` was terminated on line 16 by signal 2
```

When I configure the `#[no-exit-message]` attribute on a recipe, it doesn't seem to silence signal 2 (`SIGINT`) error messages. Is this intentional or a misdesigned approach? I'd appreciate any help. Thanks!




Discussion:

Patch:
diff --git a/src/error.rs b/src/error.rs
index 4f043a847e..0c4f11fe85 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -195,8 +195,9 @@ pub(crate) enum Error<'src> {
     recipe: &'src str,
   },
   Signal {
-    recipe: &'src str,
     line_number: Option<usize>,
+    print_message: bool,
+    recipe: &'src str,
     signal: i32,
   },
   #[cfg(windows)]
@@ -228,8 +229,9 @@ pub(crate) enum Error<'src> {
     io_error: io::Error,
   },
   Unknown {
-    recipe: &'src str,
     line_number: Option<usize>,
+    print_message: bool,
+    recipe: &'src str,
   },
   UnknownGroup {
     group: String,
@@ -296,6 +298,29 @@ impl<'src> Error<'src> {
     }
   }
 
+  /// `Self::Signal` if process was terminated by a signal otherwise
+  /// `Self::UnknownFailure`.
+  pub(crate) fn from_signal(
+    exit_status: ExitStatus,
+    line_number: Option<usize>,
+    print_message: bool,
+    recipe: &'src str,
+  ) -> Self {
+    match Platform::signal_from_exit_status(exit_status) {
+      Some(signal) => Self::Signal {
+        line_number,
+        print_message,
+        recipe,
+        signal,
+      },
+      None => Self::Unknown {
+        line_number,
+        print_message,
+        recipe,
+      },
+    }
+  }
+
   pub(crate) fn internal(message: impl Into<String>) -> Self {
     Self::Internal {
       message: message.into(),
@@ -303,13 +328,12 @@ impl<'src> Error<'src> {
   }
 
   pub(crate) fn print_message(&self) -> bool {
-    !matches!(
-      self,
-      Error::Code {
-        print_message: false,
-        ..
-      }
-    )
+    match self {
+      Self::Code { print_message, .. }
+      | Self::Signal { print_message, .. }
+      | Self::Unknown { print_message, .. } => *print_message,
+      _ => true,
+    }
   }
 
   fn source(&self) -> Option<&dyn std::error::Error> {
@@ -717,6 +741,7 @@ impl ColorDisplay for Error<'_> {
         recipe,
         line_number,
         signal,
+        ..
       } => {
         if let Some(n) = line_number {
           write!(
@@ -766,6 +791,7 @@ impl ColorDisplay for Error<'_> {
       Unknown {
         recipe,
         line_number,
+        ..
       } => {
         if let Some(n) = line_number {
           write!(
diff --git a/src/recipe.rs b/src/recipe.rs
index b7cca58a39..fa6fe9cdc9 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -1,21 +1,5 @@
 use super::*;
 
-/// Return a `Error::Signal` if the process was terminated by a signal,
-/// otherwise return an `Error::UnknownFailure`
-fn error_from_signal(recipe: &str, line_number: Option<usize>, exit_status: ExitStatus) -> Error {
-  match Platform::signal_from_exit_status(exit_status) {
-    Some(signal) => Error::Signal {
-      recipe,
-      line_number,
-      signal,
-    },
-    None => Error::Unknown {
-      recipe,
-      line_number,
-    },
-  }
-}
-
 /// A recipe, e.g. `foo: bar baz`
 #[derive(PartialEq, Debug, Clone, Serialize)]
 pub(crate) struct Recipe<'src, D = Dependency<'src>> {
@@ -374,10 +358,11 @@ impl<'src, D> Recipe<'src, D> {
               }
             }
           } else if !infallible {
-            return Err(error_from_signal(
-              self.name(),
-              Some(line_number),
+            return Err(Error::from_signal(
               exit_status,
+              Some(line_number),
+              self.print_exit_message(settings),
+              self.name(),
             ));
           }
         }
@@ -521,7 +506,14 @@ impl<'src, D> Recipe<'src, D> {
 
     match result {
       Ok(exit_status) => exit_status.code().map_or_else(
-        || Err(error_from_signal(self.name(), None, exit_status)),
+        || {
+          Err(Error::from_signal(
+            exit_status,
+            None,
+            self.print_exit_message(&context.module.settings),
+            self.name(),
+          ))
+        },
         |code| {
           if code == 0 {
             Ok(())


Test patch:
diff --git a/tests/no_exit_message.rs b/tests/no_exit_message.rs
index ff79d9a405..b607d25b0b 100644
--- a/tests/no_exit_message.rs
+++ b/tests/no_exit_message.rs
@@ -262,3 +262,49 @@ fn exit_message_and_no_exit_message_compile_forbidden() {
     )
     .failure();
 }
+
+#[test]
+#[cfg(unix)]
+fn signal_exit_message_suppressed() {
+  Test::new()
+    .justfile(
+      "
+        [no-exit-message]
+        default:
+          #!/usr/bin/env sh
+          kill -TERM $$
+      ",
+    )
+    .status(128 + 15);
+}
+
+#[test]
+#[cfg(unix)]
+fn signal_exit_message_not_suppressed() {
+  Test::new()
+    .justfile(
+      "
+        default:
+          #!/usr/bin/env sh
+          kill -TERM $$
+      ",
+    )
+    .stderr("error: Recipe `default` was terminated by signal 15\n")
+    .status(128 + 15);
+}
+
+#[test]
+#[cfg(unix)]
+fn signal_exit_message_setting_suppressed() {
+  Test::new()
+    .justfile(
+      "
+        set no-exit-message
+
+        default:
+          #!/usr/bin/env sh
+          kill -TERM $$
+      ",
+    )
+    .status(128 + 15);
+}