Bug 578511 - jgit client does not receive NoRemoteRepositoryException
Summary: jgit client does not receive NoRemoteRepositoryException
Status: RESOLVED FIXED
Alias: None
Product: JGit
Classification: Technology
Component: JGit (show other bugs)
Version: 5.11   Edit
Hardware: PC Mac OS X
: P3 normal (vote)
Target Milestone: 6.1   Edit
Assignee: Project Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2022-02-01 09:41 EST by Antonio Barone CLA
Modified: 2022-02-08 05:57 EST (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Antonio Barone CLA 2022-02-01 09:41:48 EST
The problem
------------

One of our git daemons has a problem and errors when a client pushes to an inexistent repo, throwing:

```
java.io.EOFException: Short read of block.
```

This exception is then caught by jgit and a `NoRemoteRepositoryException` is constructed but does not propagate to the caller.


What is the expected output?
------------

The client (either gerrit or jgit standalone) should receive the NoRemoteRepositoryException.


What do you see instead?
------------

The client (either Gerrit or jgit standalone) receives the following exception instead:

```
Caused by: org.eclipse.jgit.api.errors.TransportException: Can't overwrite cause with java.io.EOFException: Short read of block.
```


Please provide any additional information below.

The issue, introduced here [1], is that jgit attempts to call `initCause()` on an exception that already has a cause.

This, in turn, raises an IllegalStateException:

```
Caused by: java.lang.IllegalStateException: Can't overwrite cause with java.io.EOFException: Short read of block.
```

The consequence of this issue is that Gerrit (specifically, the replication plugin) fails to recognize that the exception was raised due to a `NoRemoteRepositoryException` and thus does not trigger the creation of the remote repository (see [2] for more details).

Here is the full stack trace:

```
org.eclipse.jgit.errors.TransportException: Can't overwrite cause with java.io.EOFException: Short read of block.
        at org.eclipse.jgit.transport.BasePackConnection.readAdvertisedRefs(BasePackConnection.java:185)
        at org.eclipse.jgit.transport.TransportGitAnon$TcpPushConnection.<init>(TransportGitAnon.java:234)
        at org.eclipse.jgit.transport.TransportGitAnon.openPush(TransportGitAnon.java:108)
        at org.eclipse.jgit.transport.PushProcess.execute(PushProcess.java:127)
        at org.eclipse.jgit.transport.Transport.push(Transport.java:1384)
        at org.eclipse.jgit.transport.Transport.push(Transport.java:1430)
        at com.googlesource.gerrit.plugins.replication.PushOne.pushInBatches(PushOne.java:576)
        at com.googlesource.gerrit.plugins.replication.PushOne.pushVia(PushOne.java:569)
        at com.googlesource.gerrit.plugins.replication.PushOne.runImpl(PushOne.java:540)
        at com.googlesource.gerrit.plugins.replication.PushOne.doRunPushOperation(PushOne.java:423)
        at com.googlesource.gerrit.plugins.replication.PushOne.runPushOperation(PushOne.java:391)
        at com.googlesource.gerrit.plugins.replication.PushOne.lambda$run$2(PushOne.java:377)
        at com.google.gerrit.server.util.RequestScopePropagator.lambda$cleanup$1(RequestScopePropagator.java:186)
        at com.google.gerrit.server.util.RequestScopePropagator.lambda$context$0(RequestScopePropagator.java:174)
        at com.google.gerrit.server.git.PerThreadRequestScope$Propagator.lambda$scope$0(PerThreadRequestScope.java:70)
        at com.googlesource.gerrit.plugins.replication.PushOne.run(PushOne.java:380)
        at com.google.gerrit.server.logging.LoggingContextAwareRunnable.run(LoggingContextAwareRunnable.java:95)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
        at com.google.gerrit.server.git.WorkQueue$Task.run(WorkQueue.java:612)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.IllegalStateException: Can't overwrite cause with java.io.EOFException: Short read of block.
        at java.base/java.lang.Throwable.initCause(Throwable.java:462)
        at org.eclipse.jgit.transport.BasePackConnection.readAdvertisedRefsImpl(BasePackConnection.java:214)
        at org.eclipse.jgit.transport.BasePackConnection.readAdvertisedRefs(BasePackConnection.java:179)
        ... 23 more
Caused by: org.eclipse.jgit.errors.NoRemoteRepositoryException: REPO: not found.
        at org.eclipse.jgit.transport.BasePackConnection.noRepository(BasePackConnection.java:574)
        at org.eclipse.jgit.transport.BasePackConnection.readAdvertisedRefsImpl(BasePackConnection.java:213)
        at org.eclipse.jgit.transport.BasePackConnection.readAdvertisedRefs(BasePackConnection.java:179)
        at org.eclipse.jgit.transport.TransportGitAnon$TcpFetchConnection.<init>(TransportGitAnon.java:193)
        at org.eclipse.jgit.transport.TransportGitAnon$TcpFetchConnection.<init>(TransportGitAnon.java:168)
        at org.eclipse.jgit.transport.TransportGitAnon.openFetch(TransportGitAnon.java:95)
        at org.eclipse.jgit.transport.BasePackPushConnection.noRepository(BasePackPushConnection.java:151)
        at org.eclipse.jgit.transport.BasePackConnection.readAdvertisedRefsImpl(BasePackConnection.java:213)
        ... 24 more
Caused by: java.io.EOFException: Short read of block.
        at org.eclipse.jgit.util.IO.readFully(IO.java:203)
        at org.eclipse.jgit.transport.PacketLineIn.readLength(PacketLineIn.java:316)
        at org.eclipse.jgit.transport.PacketLineIn.readString(PacketLineIn.java:180)
        at org.eclipse.jgit.transport.BasePackConnection.readLine(BasePackConnection.java:190)
        at org.eclipse.jgit.transport.BasePackConnection.readAdvertisedRefsImpl(BasePackConnection.java:211)
        ... 30 more
```

[1]https://git.eclipse.org/r/c/jgit/jgit/+/172595/9/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java#214

[2] https://bugs.chromium.org/p/gerrit/issues/detail?id=15538
Comment 1 Thomas Wolf CLA 2022-02-01 17:46:14 EST
Oi, that's convoluted. BasePackPushConnection overrides noRepository() and initiates a fetch, which may throw an already configured NoRemoteRepositoryException. Nasty. Something like

  if (noRepo.getCause() == null) {
    noRepo.initCause(e);
  } else {
    noRepo.addSuppressed(e);
  }

should resolve this, shouldn't it?
Comment 2 Antonio Barone CLA 2022-02-02 02:39:56 EST
Yes @Thomas, that's exactly what happens, we got to the same conclusion and to a very similar fix.

We wanted first to have a failing test to surface the problem [1].
Likely today we'll push a fix.

Also, looking at the existing code, this might not be the only case where we blindly `initCause` without first checking whether the exception already has one, so it might not be an isolated case.

Once we get this out perhaps we should be looking at other possible occurrences.

[1]https://git.eclipse.org/r/c/jgit/jgit/+/190250
Comment 3 Thomas Wolf CLA 2022-02-02 02:48:41 EST
All the other use of initCause() are fine. They all do

  Throwable t = new SomeException(someMessage);
  t.initCause(e);
  throw t;

There are two more that do

  Throwable t = someMethodReturningAnException();
  t.initCause(e);
  throw t;

but in those two cases the method is private and does not set the cause.

The one you found is the only case where the method is overrideable.
Comment 4 Antonio Barone CLA 2022-02-02 02:56:29 EST
Yep, you're right, I also just scanned through the code and found no other problematic usage of initCause, good stuff.

Let's just get this one fixed then.