Encode some execution failures with FailureDetails

Encodes failures in WorkspaceStatus and JavaCompile actions, and
ActionRewindStrategy, with FailureDetails. Removes one undetailed
ActionExecutionException overload.

RELNOTES: None.
PiperOrigin-RevId: 315339908
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java
index a3c458e..8c254e1 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java
@@ -39,15 +39,6 @@
   private final DetailedExitCode detailedExitCode;
 
   public ActionExecutionException(
-      Throwable cause, ActionAnalysisMetadata action, boolean catastrophe) {
-    super(cause.getMessage(), cause);
-    this.action = action;
-    this.detailedExitCode = DetailedExitCode.justExitCode(ExitCode.BUILD_FAILURE);
-    this.rootCauses = rootCausesFromAction(action, detailedExitCode);
-    this.catastrophe = catastrophe;
-  }
-
-  public ActionExecutionException(
       Throwable cause,
       ActionAnalysisMetadata action,
       boolean catastrophe,
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
index be48eb2..8ae3bdf 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
@@ -20,6 +20,7 @@
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -69,11 +70,15 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.exec.SpawnStrategyResolver;
+import com.google.devtools.build.lib.server.FailureDetails;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.Spawn.Code;
 import com.google.devtools.build.lib.skylarkbuildapi.CommandLineArgsApi;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.Location;
 import com.google.devtools.build.lib.syntax.Sequence;
 import com.google.devtools.build.lib.syntax.StarlarkList;
+import com.google.devtools.build.lib.util.DetailedExitCode;
 import com.google.devtools.build.lib.util.Fingerprint;
 import com.google.devtools.build.lib.util.LazyString;
 import com.google.devtools.build.lib.util.Pair;
@@ -323,7 +328,7 @@
       throw toActionExecutionException(
           new EnvironmentalExecException(e), actionExecutionContext.showVerboseFailures(label));
     } catch (CommandLineExpansionException e) {
-      throw new ActionExecutionException(e, this, /*catastrophe=*/ false);
+      throw createDetailedException(e, Code.COMMAND_LINE_EXPANSION_FAILURE);
     }
     SpawnContinuation spawnContinuation =
         actionExecutionContext
@@ -332,6 +337,16 @@
     return new SpawnActionContinuation(actionExecutionContext, spawnContinuation, label);
   }
 
+  private ActionExecutionException createDetailedException(Exception e, Code detailedCode) {
+    DetailedExitCode detailedExitCode =
+        DetailedExitCode.of(
+            FailureDetail.newBuilder()
+                .setMessage(Strings.nullToEmpty(e.getMessage()))
+                .setSpawn(FailureDetails.Spawn.newBuilder().setCode(detailedCode))
+                .build());
+    return new ActionExecutionException(e, this, /*catastrophe=*/ false, detailedExitCode);
+  }
+
   private ActionExecutionException toActionExecutionException(
       ExecException e, boolean verboseFailures) {
     String failMessage;
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java
index c4b0650..8580ee1 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java
@@ -17,6 +17,7 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.stream.Collectors.joining;
 
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -42,11 +43,16 @@
 import com.google.devtools.build.lib.runtime.Command;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
 import com.google.devtools.build.lib.runtime.WorkspaceBuilder;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.WorkspaceStatus;
+import com.google.devtools.build.lib.server.FailureDetails.WorkspaceStatus.Code;
+import com.google.devtools.build.lib.shell.AbnormalTerminationException;
 import com.google.devtools.build.lib.shell.BadExitStatusException;
 import com.google.devtools.build.lib.shell.CommandException;
 import com.google.devtools.build.lib.shell.CommandResult;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.util.CommandBuilder;
+import com.google.devtools.build.lib.util.DetailedExitCode;
 import com.google.devtools.build.lib.util.Fingerprint;
 import com.google.devtools.build.lib.util.NetUtil;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
@@ -113,15 +119,20 @@
       } catch (BadExitStatusException e) {
         String errorMessage = e.getMessage();
         try {
-          actionExecutionContext.getFileOutErr().getOutputStream().write(
-              e.getResult().getStdout());
+          actionExecutionContext.getFileOutErr().getOutputStream().write(e.getResult().getStdout());
           actionExecutionContext.getFileOutErr().getErrorStream().write(e.getResult().getStderr());
         } catch (IOException e2) {
           errorMessage = errorMessage + " and could not get stdout/stderr: " + e2.getMessage();
         }
-        throw new ActionExecutionException(errorMessage, e, this, true);
+        throw new ActionExecutionException(
+            errorMessage, e, this, true, createDetailedCode(errorMessage, Code.NON_ZERO_EXIT));
       } catch (CommandException e) {
-        throw new ActionExecutionException(e, this, true);
+        Code detailedCode =
+            e instanceof AbnormalTerminationException
+                ? Code.ABNORMAL_TERMINATION
+                : Code.EXEC_FAILED;
+        throw new ActionExecutionException(
+            e, this, true, createDetailedCode(Strings.nullToEmpty(e.getMessage()), detailedCode));
       }
       return "";
     }
@@ -262,6 +273,14 @@
     }
   }
 
+  private static DetailedExitCode createDetailedCode(String message, Code detailedCode) {
+    return DetailedExitCode.of(
+        FailureDetail.newBuilder()
+            .setMessage(message)
+            .setWorkspaceStatus(WorkspaceStatus.newBuilder().setCode(detailedCode))
+            .build());
+  }
+
   private static class BazelStatusActionFactory implements WorkspaceStatusAction.Factory {
     @Override
     public Map<String, String> createDummyWorkspaceStatus(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/BUILD b/src/main/java/com/google/devtools/build/lib/rules/java/BUILD
index cb1c28c..446d9a8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/BUILD
@@ -219,6 +219,7 @@
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/core",
         "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java",
         "//src/main/java/com/google/devtools/build/lib/util",
+        "//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
         "//src/main/java/com/google/devtools/build/lib/util:filetype",
         "//src/main/java/com/google/devtools/build/lib/util:string",
         "//src/main/java/com/google/devtools/build/lib/vfs",
@@ -227,6 +228,7 @@
         "//src/main/java/net/starlark/java/annot",
         "//src/main/protobuf:deps_java_proto",
         "//src/main/protobuf:extra_actions_base_java_proto",
+        "//src/main/protobuf:failure_details_java_proto",
         "//third_party:auto_value",
         "//third_party:guava",
         "//third_party:jsr305",
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
index 4aaa692..d59a9af 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
@@ -18,6 +18,7 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -60,11 +61,15 @@
 import com.google.devtools.build.lib.exec.SpawnStrategyResolver;
 import com.google.devtools.build.lib.rules.java.JavaConfiguration.JavaClasspathMode;
 import com.google.devtools.build.lib.rules.java.JavaPluginInfoProvider.JavaPluginInfo;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.JavaCompile;
+import com.google.devtools.build.lib.server.FailureDetails.JavaCompile.Code;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.Location;
 import com.google.devtools.build.lib.syntax.Sequence;
 import com.google.devtools.build.lib.syntax.StarlarkList;
+import com.google.devtools.build.lib.util.DetailedExitCode;
 import com.google.devtools.build.lib.util.Fingerprint;
 import com.google.devtools.build.lib.util.LazyString;
 import com.google.devtools.build.lib.view.proto.Deps;
@@ -327,7 +332,7 @@
         try {
           reducedClasspath = getReducedClasspath(actionExecutionContext, context);
         } catch (IOException e) {
-          throw new ActionExecutionException(e, this, /*catastrophe=*/ false);
+          throw createActionExecutionException(e, Code.REDUCED_CLASSPATH_FAILURE);
         }
         spawn = getReducedSpawn(actionExecutionContext, reducedClasspath, /* fallback= */ false);
       } else {
@@ -335,7 +340,7 @@
         spawn = getFullSpawn(actionExecutionContext);
       }
     } catch (CommandLineExpansionException e) {
-      throw new ActionExecutionException(e, this, /*catastrophe=*/ false);
+      throw createActionExecutionException(e, Code.COMMAND_LINE_EXPANSION_FAILURE);
     }
     SpawnContinuation spawnContinuation =
         actionExecutionContext
@@ -549,6 +554,16 @@
     }
   }
 
+  private ActionExecutionException createActionExecutionException(Exception e, Code detailedCode) {
+    DetailedExitCode detailedExitCode =
+        DetailedExitCode.of(
+            FailureDetail.newBuilder()
+                .setMessage(Strings.nullToEmpty(e.getMessage()))
+                .setJavaCompile(JavaCompile.newBuilder().setCode(detailedCode))
+                .build());
+    return new ActionExecutionException(e, this, /*catastrophe=*/ false, detailedExitCode);
+  }
+
   private final class JavaActionContinuation extends ActionContinuationOrResult {
     private final ActionExecutionContext actionExecutionContext;
     @Nullable private final ReducedClasspath reducedClasspath;
@@ -601,7 +616,10 @@
         try {
           spawn = getReducedSpawn(actionExecutionContext, reducedClasspath, /* fallback=*/ true);
         } catch (CommandLineExpansionException e) {
-          throw new ActionExecutionException(e, JavaCompileAction.this, /*catastrophe=*/ false);
+          Code detailedCode = Code.COMMAND_LINE_EXPANSION_FAILURE;
+          ActionExecutionException actionExecutionException =
+              createActionExecutionException(e, detailedCode);
+          throw actionExecutionException;
         }
         SpawnContinuation fallbackContinuation =
             actionExecutionContext
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionRewindStrategy.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionRewindStrategy.java
index 5853ad5..0eb710f 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionRewindStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionRewindStrategy.java
@@ -44,7 +44,11 @@
 import com.google.devtools.build.lib.actions.LostInputsActionExecutionException;
 import com.google.devtools.build.lib.bugreport.BugReport;
 import com.google.devtools.build.lib.events.ExtendedEventHandler;
+import com.google.devtools.build.lib.server.FailureDetails.ActionRewinding;
+import com.google.devtools.build.lib.server.FailureDetails.ActionRewinding.Code;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
 import com.google.devtools.build.lib.skyframe.proto.ActionRewind.ActionRewindEvent;
+import com.google.devtools.build.lib.util.DetailedExitCode;
 import com.google.devtools.build.skyframe.SkyFunction.Environment;
 import com.google.devtools.build.skyframe.SkyFunction.Restart;
 import com.google.devtools.build.skyframe.SkyKey;
@@ -204,14 +208,14 @@
       lostInputRecordsThisAction.add(lostInputRecord);
       int priorLosses = lostInputRecords.add(lostInputRecord, /*occurrences=*/ 1);
       if (MAX_REPEATED_LOST_INPUTS <= priorLosses) {
-        BugReport.sendBugReport(
-            new IllegalStateException(
-                String.format(
-                    "lost input too many times (#%s) for the same action. lostInput: %s, "
-                        + "lostInput digest: %s, failedAction: %.10000s",
-                    priorLosses + 1, lostInputsByDigest.get(digest), digest, failedAction)));
-        throw new ActionExecutionException(
-            lostInputsException, failedAction, /*catastrophe=*/ false);
+        String message =
+            String.format(
+                "lost input too many times (#%s) for the same action. lostInput: %s, "
+                    + "lostInput digest: %s, failedAction: %.10000s",
+                priorLosses + 1, lostInputsByDigest.get(digest), digest, failedAction);
+        BugReport.sendBugReport(new IllegalStateException(message));
+        throw createActionExecutionException(
+            lostInputsException, failedAction, message, Code.LOST_INPUT_TOO_MANY_TIMES);
       } else if (0 < priorLosses) {
         logger.atInfo().log(
             "lost input again (#%s) for the same action. lostInput: %s, "
@@ -308,12 +312,13 @@
       return;
     }
     // TODO(b/19539699): tighten signatures for ActionInputDepOwnerMap to make this impossible.
-    BugReport.sendBugReport(
-        new IllegalStateException(
-            String.format(
-                "Unexpected source artifact as lost input%s: %s %s",
-                lostInputQualifier, expectedDerived, failedAction)));
-    throw new ActionExecutionException(lostInputsException, failedAction, /*catastrophe=*/ false);
+    String message =
+        String.format(
+            "Unexpected source artifact as lost input%s: %s %s",
+            lostInputQualifier, expectedDerived, failedAction);
+    BugReport.sendBugReport(new IllegalStateException(message));
+    throw createActionExecutionException(
+        lostInputsException, failedAction, message, Code.LOST_INPUT_IS_SOURCE);
   }
 
   /**
@@ -575,6 +580,22 @@
     }
   }
 
+  private static ActionExecutionException createActionExecutionException(
+      LostInputsActionExecutionException lostInputsException,
+      Action failedAction,
+      String message,
+      Code detailedCode) {
+    return new ActionExecutionException(
+        lostInputsException,
+        failedAction,
+        /*catastrophe=*/ false,
+        DetailedExitCode.of(
+            FailureDetail.newBuilder()
+                .setMessage(message)
+                .setActionRewinding(ActionRewinding.newBuilder().setCode(detailedCode))
+                .build()));
+  }
+
   static class RewindPlan {
     private final Restart nodesToRestart;
     private final ImmutableList<Action> additionalActionsToRestart;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
index 77e0c68..0ee2402 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -296,6 +296,7 @@
         "//src/main/java/com/google/devtools/build/lib/util",
         "//src/main/java/com/google/devtools/build/lib/util:TestType",
         "//src/main/java/com/google/devtools/build/lib/util:abrupt_exit_exception",
+        "//src/main/java/com/google/devtools/build/lib/util:crash_failure_details",
         "//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
         "//src/main/java/com/google/devtools/build/lib/util:exit_code",
         "//src/main/java/com/google/devtools/build/lib/util:resource_usage",
@@ -456,8 +457,10 @@
         "//src/main/java/com/google/devtools/build/lib/bugreport",
         "//src/main/java/com/google/devtools/build/lib/events",
         "//src/main/java/com/google/devtools/build/lib/skyframe/proto:action_rewind_event_java_proto",
+        "//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
+        "//src/main/protobuf:failure_details_java_proto",
         "//third_party:auto_value",
         "//third_party:flogger",
         "//third_party:guava",
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
index 5ca7f6c..7a002d4 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
@@ -88,6 +88,7 @@
 import com.google.devtools.build.lib.skyframe.ActionExecutionState.ActionStep;
 import com.google.devtools.build.lib.skyframe.ActionExecutionState.ActionStepOrResult;
 import com.google.devtools.build.lib.skyframe.ActionExecutionState.SharedActionCallback;
+import com.google.devtools.build.lib.util.CrashFailureDetails;
 import com.google.devtools.build.lib.util.io.FileOutErr;
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
@@ -1073,7 +1074,11 @@
             action,
             actionResult,
             actionFileSystemType().inMemoryFileSystem(),
-            new ActionExecutionException(exception, action, true),
+            new ActionExecutionException(
+                exception,
+                action,
+                true,
+                CrashFailureDetails.detailedExitCodeForThrowable(exception)),
             fileOutErr,
             ErrorTiming.AFTER_EXECUTION);
         throw exception;
diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto
index aad05b5..8cb351d 100644
--- a/src/main/protobuf/failure_details.proto
+++ b/src/main/protobuf/failure_details.proto
@@ -128,6 +128,9 @@
     RunCommand run_command = 152;
     VersionCommand version_command = 153;
     PrintActionCommand print_action_command = 154;
+    WorkspaceStatus workspace_status = 158;
+    JavaCompile java_compile = 159;
+    ActionRewinding action_rewinding = 160;
   }
 
   reserved 102; // For internal use
@@ -184,6 +187,7 @@
     EXECUTION_FAILED = 4 [(metadata) = { exit_code: 1 }];
     EXECUTION_DENIED = 5 [(metadata) = { exit_code: 1 }];
     REMOTE_CACHE_FAILED = 6 [(metadata) = { exit_code: 34 }];
+    COMMAND_LINE_EXPANSION_FAILURE = 7 [(metadata) = { exit_code: 1 }];
   }
   Code code = 1;
 
@@ -767,3 +771,34 @@
 
   Code code = 1;
 }
+
+message WorkspaceStatus {
+  enum Code {
+    WORKSPACE_STATUS_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
+    NON_ZERO_EXIT = 1 [(metadata) = { exit_code: 1 }];
+    ABNORMAL_TERMINATION = 2 [(metadata) = { exit_code: 1 }];
+    EXEC_FAILED = 3 [(metadata) = { exit_code: 1 }];
+  }
+
+  Code code = 1;
+}
+
+message JavaCompile {
+  enum Code {
+    JAVA_COMPILE_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
+    REDUCED_CLASSPATH_FAILURE = 1 [(metadata) = { exit_code: 1 }];
+    COMMAND_LINE_EXPANSION_FAILURE = 2 [(metadata) = { exit_code: 1 }];
+  }
+
+  Code code = 1;
+}
+
+message ActionRewinding {
+  enum Code {
+    ACTION_REWINDING_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
+    LOST_INPUT_TOO_MANY_TIMES = 1 [(metadata) = { exit_code: 1 }];
+    LOST_INPUT_IS_SOURCE = 2 [(metadata) = { exit_code: 1 }];
+  }
+
+  Code code = 1;
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestUtil.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestUtil.java
index 30e86d5..91c8873 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestUtil.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestUtil.java
@@ -50,6 +50,7 @@
 import com.google.devtools.build.lib.shell.Command;
 import com.google.devtools.build.lib.syntax.StarlarkSemantics;
 import com.google.devtools.build.lib.testutil.TestConstants;
+import com.google.devtools.build.lib.util.CrashFailureDetails;
 import com.google.devtools.build.lib.util.Fingerprint;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -244,7 +245,8 @@
         FileSystemUtils.writeContent(
             actionExecutionContext.getInputPath(volatileStatus), new byte[] {});
       } catch (IOException e) {
-        throw new ActionExecutionException(e, this, true);
+        throw new ActionExecutionException(
+            e, this, true, CrashFailureDetails.detailedExitCodeForThrowable(e));
       }
       return ActionResult.EMPTY;
     }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD b/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD
index 0723ee0..644b8d2 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD
@@ -111,6 +111,7 @@
         "//src/main/java/com/google/devtools/build/lib/skyframe:unloaded_toolchain_context",
         "//src/main/java/com/google/devtools/build/lib/skyframe/packages:PackageFactoryBuilderWithSkyframeForTesting",
         "//src/main/java/com/google/devtools/build/lib/util",
+        "//src/main/java/com/google/devtools/build/lib/util:crash_failure_details",
         "//src/main/java/com/google/devtools/build/lib/util:filetype",
         "//src/main/java/com/google/devtools/build/lib/util:string",
         "//src/main/java/com/google/devtools/build/lib/util/io",
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
index d97bbd7..cf471d2 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -236,6 +236,7 @@
         "//src/main/java/net/starlark/java/annot",
         "//src/main/java/com/google/devtools/build/lib/util",
         "//src/main/java/com/google/devtools/build/lib/util:abrupt_exit_exception",
+        "//src/main/java/com/google/devtools/build/lib/util:crash_failure_details",
         "//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
         "//src/main/java/com/google/devtools/build/lib/util:filetype",
         "//src/main/java/com/google/devtools/build/lib/util/io",
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
index 17c3fcf..130acba 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
@@ -121,6 +121,7 @@
 import com.google.devtools.build.lib.syntax.StarlarkSemantics;
 import com.google.devtools.build.lib.testutil.MoreAsserts;
 import com.google.devtools.build.lib.testutil.TestUtils;
+import com.google.devtools.build.lib.util.CrashFailureDetails;
 import com.google.devtools.build.lib.util.DetailedExitCode;
 import com.google.devtools.build.lib.util.Fingerprint;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
@@ -1459,7 +1460,8 @@
       try {
         FileSystemUtils.createEmptyFile(actionExecutionContext.getInputPath(getPrimaryOutput()));
       } catch (IOException e) {
-        throw new ActionExecutionException(e, this, false);
+        throw new ActionExecutionException(
+            e, this, false, CrashFailureDetails.detailedExitCodeForThrowable(e));
       }
       return ActionResult.EMPTY;
     }
@@ -1880,7 +1882,10 @@
               @Override
               public Void call() throws ActionExecutionException {
                 throw new ActionExecutionException(
-                    new Exception(), failedActionReference.get(), /*catastrophe=*/ false);
+                    new Exception(),
+                    failedActionReference.get(),
+                    /*catastrophe=*/ false,
+                    USER_DETAILED_EXIT_CODE);
               }
             },
             NestedSetBuilder.emptySet(Order.STABLE_ORDER),
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java
index 4374c38..1c96330 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java
@@ -36,6 +36,7 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.testutil.TimestampGranularityUtils;
+import com.google.devtools.build.lib.util.CrashFailureDetails;
 import com.google.devtools.build.lib.util.Fingerprint;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -201,7 +202,8 @@
       try (InputStream in = getInputs().getSingleton().getPath().getInputStream()) {
         inputLen = in.read(input);
       } catch (IOException e) {
-        throw new ActionExecutionException(e, this, false);
+        throw new ActionExecutionException(
+            e, this, false, CrashFailureDetails.detailedExitCodeForThrowable(e));
       }
 
       // This action then writes the contents of the input to the (only) output file, and appends an
@@ -210,7 +212,8 @@
         out.write(input, 0, inputLen);
         out.write('x');
       } catch (IOException e) {
-        throw new ActionExecutionException(e, this, false);
+        throw new ActionExecutionException(
+            e, this, false, CrashFailureDetails.detailedExitCodeForThrowable(e));
       }
       return ActionResult.EMPTY;
     }
@@ -696,7 +699,8 @@
       try (InputStream in = getPrimaryInput().getPath().getInputStream()) {
         inputLen = in.read(input, 0, input.length);
       } catch (IOException e) {
-        throw new ActionExecutionException(e, this, false);
+        throw new ActionExecutionException(
+            e, this, false, CrashFailureDetails.detailedExitCodeForThrowable(e));
       }
       return new Buffer(input, inputLen);
     }
@@ -708,7 +712,8 @@
         }
         out.write(data.getBytes(StandardCharsets.UTF_8), 0, data.length());
       } catch (IOException e) {
-        throw new ActionExecutionException(e, this, false);
+        throw new ActionExecutionException(
+            e, this, false, CrashFailureDetails.detailedExitCodeForThrowable(e));
       }
     }
 
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
index 4a11445..70ceece 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
@@ -56,6 +56,7 @@
 import com.google.devtools.build.lib.skyframe.ActionTemplateExpansionValue.ActionTemplateExpansionKey;
 import com.google.devtools.build.lib.skyframe.serialization.testutils.SerializationTester;
 import com.google.devtools.build.lib.testutil.TestUtils;
+import com.google.devtools.build.lib.util.CrashFailureDetails;
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
@@ -849,7 +850,8 @@
       try {
         run(context);
       } catch (IOException e) {
-        throw new ActionExecutionException(e, this, /*catastrophe=*/ false);
+        throw new ActionExecutionException(
+            e, this, /*catastrophe=*/ false, CrashFailureDetails.detailedExitCodeForThrowable(e));
       }
       return ActionResult.EMPTY;
     }