Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-60308

CompositeIOException is unhelpful for diagnostics

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: Major Major
    • core
    • None

      The causes of a CompositeIoException are lost when the exception is rethrown as part of a chain, (ie it is a cause in another Throwable).

       

      This is because Throwable.printStackTrace() does not call printStackTrace on its causes but rather printEnclosedStackTrace to handle circular references and prevent a thread loop.

       

      This is exceptionally painful when upgrading to 2.204 for the most recent LTS as something in that chain causes tests to fail if the temporary JenkinsHome of a JenkinsRule Test can not be cleans.

      it results in failures like:

      [2019-11-27T17:04:38.004Z] [ERROR] com.cloudbees.Blah.someTest  Time elapsed: 7.157 s  <<< ERROR!
      [2019-11-27T17:04:38.004Z] java.io.IOException: Failed to clean up temp dirs
      [2019-11-27T17:04:38.004Z] 	at org.jvnet.hudson.test.TemporaryDirectoryAllocator.dispose(TemporaryDirectoryAllocator.java:96)
      [2019-11-27T17:04:38.004Z] 	at org.jvnet.hudson.test.TestEnvironment.dispose(TestEnvironment.java:84)
      [2019-11-27T17:04:38.004Z] 	at org.jvnet.hudson.test.JenkinsRule.after(JenkinsRule.java:511)
      [2019-11-27T17:04:38.004Z] 	at org.jvnet.hudson.test.JenkinsRule$1.evaluate(JenkinsRule.java:614)
      [2019-11-27T17:04:38.004Z] 	at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:298)
      [2019-11-27T17:04:38.004Z] 	at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:292)
      [2019-11-27T17:04:38.004Z] 	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      [2019-11-27T17:04:38.004Z] 	at java.lang.Thread.run(Thread.java:748)
      [2019-11-27T17:04:38.004Z] Caused by: jenkins.util.io.CompositeIOException: Unable to delete 'C:\e4909a8e\workspace\operations-center-context_PR-382\target\tmp\j h2709724025285257367'. Tried 3 times (of a maximum of 3) waiting 0.1 sec between attempts.
      [2019-11-27T17:04:38.004Z] 	at jenkins.util.io.PathRemover.forceRemoveRecursive(PathRemover.java:99)
      [2019-11-27T17:04:38.004Z] 	at hudson.Util.deleteRecursive(Util.java:293)
      [2019-11-27T17:04:38.004Z] 	at hudson.FilePath$DeleteRecursive.invoke(FilePath.java:1271)
      [2019-11-27T17:04:38.004Z] 	at hudson.FilePath$DeleteRecursive.invoke(FilePath.java:1267)
      [2019-11-27T17:04:38.004Z] 	at hudson.FilePath.act(FilePath.java:1075)
      [2019-11-27T17:04:38.004Z] 	at hudson.FilePath.act(FilePath.java:1058)
      [2019-11-27T17:04:38.004Z] 	at hudson.FilePath.deleteRecursive(FilePath.java:1265)
      [2019-11-27T17:04:38.004Z] 	at org.jvnet.hudson.test.TemporaryDirectoryAllocator.dispose(TemporaryDirectoryAllocator.java:91)
      [2019-11-27T17:04:38.004Z] 	... 7 more

      This is exceptionally painful on windows where you can not delete a directory if the same process has files open in it, and when the failure is on a CI system and the developers all have Linux/Mac then it becomes even harder.

      without knowing which file(s) could not be deleted you are effectively fishing.   You can inspect the exception if you are lucky enough to trap it locally to obtain the info, but this is unlikely to happen for a running Jenkins instance or a test running in CI.

          [JENKINS-60308] CompositeIOException is unhelpful for diagnostics

          James Nord added a comment -

          this is highly obvious in the TestHarness code https://github.com/jenkinsci/jenkins-test-harness/blob/e72678d347373228b6d81097d792e968376a6aa1/src/main/java/org/jvnet/hudson/test/TemporaryDirectoryAllocator.java#L87-L97 

              /**
               * Deletes all allocated temporary directories.
               */
              public synchronized void dispose() throws IOException, InterruptedException {
                  IOException x = null;
                  for (File dir : tmpDirectories)
                      try {
                          new FilePath(dir).deleteRecursive();
                      } catch (IOException e) {
                          x = e;
                      }
                  tmpDirectories.clear();
                  if (x!=null)    throw new IOException("Failed to clean up temp dirs",x);
              }

          the IoException causes the actual cause to be lost (and only the most recent one is throw,)  this specific case can be fixed by actually throwing a CompositeIOException - but we can not control all the code that catches and handles exceptions.

          James Nord added a comment - this is highly obvious in the TestHarness code  https://github.com/jenkinsci/jenkins-test-harness/blob/e72678d347373228b6d81097d792e968376a6aa1/src/main/java/org/jvnet/hudson/test/TemporaryDirectoryAllocator.java#L87-L97   /** * Deletes all allocated temporary directories. */ public synchronized void dispose() throws IOException, InterruptedException { IOException x = null ; for (File dir : tmpDirectories) try { new FilePath(dir).deleteRecursive(); } catch (IOException e) { x = e; } tmpDirectories.clear(); if (x!= null ) throw new IOException( "Failed to clean up temp dirs" ,x); } the IoException causes the actual cause to be lost (and only the most recent one is throw,)  this specific case can be fixed by actually throwing a CompositeIOException - but we can not control all the code that catches and handles exceptions.

          Jesse Glick added a comment -

          Fixed as part of JENKINS-60716.

          Jesse Glick added a comment - Fixed as part of JENKINS-60716 .

            Unassigned Unassigned
            teilo James Nord
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: