Omit redundant 'Error in fail:' prefix from Starlark fail() error messages.
Calling `fail()` in Starlark previously formatted the final error line with `Error in fail: <message>` because `fail` is a builtin function and stack trace formatting prepended `Error in <builtin>: `. Because `fail()` is the standard mechanism in Starlark for rules, macros, and extensions to raise user-facing errors (such as invalid attribute values or unsupported targets), this prefix was redundant and made user errors look like internal failures within the `fail` function itself.
Format leaf `fail()` errors using the standard `Error: ` prefix, while preserving `Error in <builtin>: ` for other builtin functions (such as `getattr()`, `struct()`, or `dict()`).
For example, when a rule implementation calls `fail("unsupported platform: %s" % ctx.attr.platform)`:
Before:
```
Traceback (most recent call last):
File "/workspace/pkg/rules.bzl", line 5, column 9, in _impl
fail("unsupported platform: %s" % ctx.attr.platform)
Error in fail: unsupported platform: foo
```
After:
```
Traceback (most recent call last):
File "/workspace/pkg/rules.bzl", line 5, column 9, in _impl
fail("unsupported platform: %s" % ctx.attr.platform)
Error: unsupported platform: foo
```
RELNOTES: Starlark `fail()` calls now format error messages with `Error: ` instead of `Error in fail: `.
Resolves https://github.com/bazelbuild/bazel/issues/21523.
PiperOrigin-RevId: 971759352
Change-Id: Idda5352f05c8d7c297261ca65e4d2906369513a8
diff --git a/src/main/java/net/starlark/java/eval/EvalException.java b/src/main/java/net/starlark/java/eval/EvalException.java
index dab368c..717bace 100644
--- a/src/main/java/net/starlark/java/eval/EvalException.java
+++ b/src/main/java/net/starlark/java/eval/EvalException.java
@@ -204,10 +204,11 @@
int n = callstack.size(); // n > 0
String prefix = "Error: ";
// If the topmost frame is a built-in, don't show it.
- // Instead just prefix the name of the built-in onto the error message.
+ // Instead just prefix the name of the built-in onto the error message,
+ // unless it is fail() where we just use "Error: ".
StarlarkThread.CallStackEntry leaf = callstack.get(n - 1);
if (leaf.location.equals(Location.BUILTIN)) {
- prefix = "Error in " + leaf.name + ": ";
+ prefix = leaf.name.equals("fail") ? "Error: " : "Error in " + leaf.name + ": ";
n--;
}
if (n > 0) {
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/BuildInfoFileWriteActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/BuildInfoFileWriteActionTest.java
index ab9304e..dc01b1c 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/BuildInfoFileWriteActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/BuildInfoFileWriteActionTest.java
@@ -329,7 +329,7 @@
assertThat(assertThrows(ActionExecutionException.class, () -> action.execute(context)))
.hasMessageThat()
- .contains("Error in fail: starlark error");
+ .contains("Error: starlark error");
}
@Test
diff --git a/src/test/java/com/google/devtools/build/lib/buildtool/KeepGoingTest.java b/src/test/java/com/google/devtools/build/lib/buildtool/KeepGoingTest.java
index 0343697..973cc49 100644
--- a/src/test/java/com/google/devtools/build/lib/buildtool/KeepGoingTest.java
+++ b/src/test/java/com/google/devtools/build/lib/buildtool/KeepGoingTest.java
@@ -387,7 +387,7 @@
"command succeeded, but not all targets were analyzed",
"//analysiserror:foo",
"//analysiserror:bar");
- events.assertContainsError("Error in fail: BOOM!");
+ events.assertContainsError("Error: BOOM!");
assertSameConfiguredTarget("//analysiserror:bar");
}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/config/StarlarkConfigFeatureFlagTransitionFactoryTest.java b/src/test/java/com/google/devtools/build/lib/rules/config/StarlarkConfigFeatureFlagTransitionFactoryTest.java
index 6b47a45..f4681fd 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/config/StarlarkConfigFeatureFlagTransitionFactoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/config/StarlarkConfigFeatureFlagTransitionFactoryTest.java
@@ -209,7 +209,7 @@
""");
reporter.removeHandler(failFastHandler);
getConfiguredTarget("//foo:top");
- assertContainsEvent("Error in fail: Rule has failed intentionally.");
+ assertContainsEvent("Error: Rule has failed intentionally.");
}
@Test
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/EvalMacroFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/EvalMacroFunctionTest.java
index 8444ee2..3cd4dd9 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/EvalMacroFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/EvalMacroFunctionTest.java
@@ -369,7 +369,7 @@
\t\tmy_macro = macro(implementation = _impl)
\tFile "/workspace/pkg/my_macro.bzl", line 3, column 9, in _impl
\t\tfail("fail fail fail")
- Error in fail: fail fail fail\
+ Error: fail fail fail\
""");
}
@@ -510,7 +510,7 @@
\t\tfail_macro = macro(implementation = _impl)
\tFile "/workspace/pkg/fail_macro.bzl", line 3, column 9, in _impl
\t\tfail("fail fail fail")
- Error in fail: fail fail fail\
+ Error: fail fail fail\
""",
"cannot compute package piece for finalizer macro //pkg:finalize defined by"
+ " //pkg:my_finalizer.bzl%my_finalizer");
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
index 3277f5d..bd55ab4 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
@@ -378,7 +378,7 @@
\t\tmy_macro = macro(implementation = _impl)
\tFile "/workspace/pkg/my_macro.bzl", line 3, column 9, in _impl
\t\tfail("fail fail fail")
- Error in fail: fail fail fail\
+ Error: fail fail fail\
""");
if (computationMode.equals(ComputationMode.MONOLITHIC_PACKAGE)) {
assertThat(eventCollector.filtered(EventKind.ERROR)).hasSize(1);
diff --git a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java
index a9db8a9..2f54f0f 100644
--- a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java
+++ b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java
@@ -3307,7 +3307,7 @@
//
// /workspace/test/rules.bzl:7:15: Traceback (most recent call last):
// File "/workspace/test/rules.bzl", line 2, column 9, in fail_with_message
- // Error in fail: args expansion error message
+ // Error: args expansion error message
// ```
// stack=[fail_with_message@rules.bzl:2, fail@<builtin>]
@@ -3315,7 +3315,7 @@
assertThat(e)
.hasMessageThat()
.contains("File \"/workspace/test/rules.bzl\", line 2, column 9, in fail_with_message");
- assertThat(e).hasMessageThat().contains("Error in fail: args expansion error message");
+ assertThat(e).hasMessageThat().contains("Error: args expansion error message");
}
@Test
diff --git a/src/test/java/net/starlark/java/eval/MethodLibraryTest.java b/src/test/java/net/starlark/java/eval/MethodLibraryTest.java
index ca9a7b1..0f0d25e 100644
--- a/src/test/java/net/starlark/java/eval/MethodLibraryTest.java
+++ b/src/test/java/net/starlark/java/eval/MethodLibraryTest.java
@@ -779,6 +779,8 @@
customSemanticsEv.eval("fail('with_a_trace')");
} catch (EvalException e) {
assertThat(e.getMessageWithStack()).contains("Traceback (most recent call last)");
+ assertThat(e.getMessageWithStack()).contains("Error: with_a_trace");
+ assertThat(e.getMessageWithStack()).doesNotContain("Error in fail");
}
try {
diff --git a/src/test/py/bazel/bzlmod/mod_command_test.py b/src/test/py/bazel/bzlmod/mod_command_test.py
index 7a1755f..687ef70 100644
--- a/src/test/py/bazel/bzlmod/mod_command_test.py
+++ b/src/test/py/bazel/bzlmod/mod_command_test.py
@@ -264,7 +264,7 @@
'ERROR: Results may be incomplete as 1 extension failed.', stderr
)
self.assertIn('\t\tfail("ext failed")', stderr)
- self.assertIn('Error in fail: ext failed', stderr)
+ self.assertIn('Error: ext failed', stderr)
self.assertListEqual(
stdout,
[
@@ -1430,7 +1430,7 @@
stderr = '\n'.join(stderr)
self.assertIn('ext1 is being evaluated', stderr)
self.assertIn('ext2 is being evaluated', stderr)
- self.assertIn('Error in fail: ext2 failed', stderr)
+ self.assertIn('Error: ext2 failed', stderr)
self.assertIn(
'Not imported, but reported as direct dependencies by the extension'
' (may cause the build to fail):\nmissing_dep',
diff --git a/src/test/shell/integration/build_event_stream_test.sh b/src/test/shell/integration/build_event_stream_test.sh
index 3093f17..2c7c02f 100755
--- a/src/test/shell/integration/build_event_stream_test.sh
+++ b/src/test/shell/integration/build_event_stream_test.sh
@@ -1627,7 +1627,7 @@
>& "$TEST_log" && fail "Expected failure"
expect_log "unsuccessful-because-of-illegal-load.*Label '//no/such/package:f.bzl' is invalid because 'no/such/package' is not a package"
expect_log "unsuccessful-because-of-BUILD-file-syntax-error.*invalid character: '@'"
- expect_log "Error in fail: bad"
+ expect_log "Error: bad"
# On this invocation, Bazel attempts to load exactly 5 packages.
expect_log_n "PROGRESS.*Loading package" 5