Repository: opentripplanner/OpenTripPlanner (2025-07-02T14:39:14Z)

Issue:
Exception while fetching data (/planConnection) : Index 0 out of bounds for length 0
## Expected behavior
The journey planning succeeds

## Observed behavior
It crashes with the following:

```
Jul 02 11:12:51 lemon java[1462947]: 11:12:51.681 WARN [grizzly-19]  (LoggingDataFetcherExceptionHandler.java:19) Exception while fetching data (/planConnection) : Index 0 out of bounds for length 0
Jul 02 11:12:51 lemon java[1462947]: java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TripTimesForDaysIndex.<init>(TripTimesForDaysIndex.java:64)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TripTimesForDaysIndex.ofTripTimesForDay(TripTimesForDaysIndex.java:36)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TripPatternForDates.createTripTimesForDaysIndex(TripPatternForDates.java:296)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TripPatternForDates.<init>(TripPatternForDates.java:100)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.transit.request.RaptorRoutingRequestTransitDataCreator.merge(RaptorRoutingRequestTransitDataCreator.java:154)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.transit.request.RaptorRoutingRequestTransitDataCreator.createTripPatterns(RaptorRoutingRequestTransitDataCreator.java:174)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.transit.request.RaptorRoutingRequestTransitData.<init>(RaptorRoutingRequestTransitData.java:92)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.router.TransitRouter.createRequestTransitDataProvider(TransitRouter.java:339)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.router.TransitRouter.route(TransitRouter.java:127)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.router.TransitRouter.routeAndCleanupAfter(TransitRouter.java:108)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.raptoradapter.router.TransitRouter.route(TransitRouter.java:101)
Jul 02 11:12:51 lemon java[1462947]:         at org.opentripplanner.routing.algorithm.RoutingWorker.routeTransit(RoutingWorker.java:262)
Jul 02 11:12:51 lemon java[1462947]:         at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
Jul 02 11:12:51 lemon java[1462947]:         at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760)
Jul 02 11:12:51 lemon java[1462947]:         at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
Jul 02 11:12:51 lemon java[1462947]:         at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312)
Jul 02 11:12:51 lemon java[1462947]:         at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843)
Jul 02 11:12:51 lemon java[1462947]:         at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808)
Jul 02 11:12:51 lemon java[1462947]:         at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188)
```

## Version of OTP used (exact commit hash or JAR name)
2.8.0-SNAPSHOT

This is most likely a regression introduced in #6666 

## Data sets in use (links to GTFS and OSM PBF files)
Great Britain GTFS: https://dev.aubin.app/gtfs/great_britain_gtfs.zip

## Command line used to start OTP

## Router config and graph build config JSON

## Steps to reproduce the problem
Plan a journey from Farringdon to Edgware Road stations


Discussion:

Patch:
diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripTimesForDaysIndex.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripTimesForDaysIndex.java
index 9613af9002a..c41515e452f 100644
--- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripTimesForDaysIndex.java
+++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripTimesForDaysIndex.java
@@ -61,12 +61,14 @@ static TripTimesForDaysIndex ofTripTimesForDay(List<int[]> departureTimes, int[]
     int tripIndexIndex = 0;
     int i = 0;
     int j = 0;
-    int u = a[i];
+    int u = safeValueAt(a, i);
     int v = safeValueNextDayAt(list, day, j);
 
     do {
       if (u <= v) {
-        tripIndexIndex = setIndexValue(tripIndexIndex, day, i);
+        if (i < a.length) {
+          tripIndexIndex = setIndexValue(tripIndexIndex, day, i);
+        }
         ++i;
         if (i < a.length) {
           u = a[i];
@@ -86,7 +88,7 @@ static TripTimesForDaysIndex ofTripTimesForDay(List<int[]> departureTimes, int[]
             a = list.get(day);
           }
           j = 0;
-          u = a[i];
+          u = safeValueAt(a, i);
           v = safeValueNextDayAt(list, day, j);
         }
       }
@@ -139,6 +141,9 @@ static List<int[]> applyOffsets(List<int[]> list, int[] offsets) {
 
   @Override
   public String toString() {
+    if (tripIndex.length == 0) {
+      return "";
+    }
     var buf = new StringBuilder();
     for (int i = 0; i < tripIndex.length; i += 2) {
       buf.append(tripIndex[i]).append(':').append(tripIndex[i + 1]).append(' ');


Test patch:
diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripTimesForDaysIndexTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripTimesForDaysIndexTest.java
index c6e4653a2ef..a3059b6499f 100644
--- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripTimesForDaysIndexTest.java
+++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripTimesForDaysIndexTest.java
@@ -3,6 +3,7 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TripTimesForDaysIndex.applyOffsets;
 
+import com.google.common.base.Splitter;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -45,11 +46,15 @@ static List<Arguments> initializationTestCases() {
     return Arrays.stream(
       """
       # Test with one day
+      [empty]        ->  [empty]
       1              ->  0:0
       1 2            ->  0:0 0:1
       1 1            ->  0:0 0:1
 
       # Test with two days
+      |              ->  [empty]
+      1 |            ->  0:0
+      | 1            ->  1:0
       1 | 2          ->  0:0 1:0
       2 | 1          ->  1:0 0:0
       1 | 1          ->  0:0 1:0
@@ -67,6 +72,8 @@ static List<Arguments> initializationTestCases() {
       1 | 1 | 2      ->  0:0 1:0 2:0
 
       # Test all overlapping times with 3 days and 5 times
+      | 1 2 | 3 4 5  ->  1:0 1:1 2:0 2:1 2:2
+      1 2 | | 3 4 5  ->  0:0 0:1 2:0 2:1 2:2
       1 2 3 | 4 | 5  ->  0:0 0:1 0:2 1:0 2:0
       1 2 3 | 5 | 4  ->  0:0 0:1 0:2 2:0 1:0
       1 2 | 3 4 | 5  ->  0:0 0:1 1:0 1:1 2:0
@@ -108,9 +115,14 @@ static List<Arguments> initializationTestCases() {
       """.split("\n")
     )
       .map(String::trim)
-      .filter(s -> s.length() > 0)
+      .filter(s -> !s.isEmpty())
       .filter(s -> !s.startsWith("#"))
-      .map(s -> Arguments.of(s.substring(0, 14).trim(), s.substring(18).trim()))
+      .map(s ->
+        Arguments.of(
+          s.substring(0, 14).replaceAll("\\[empty]", "").trim(),
+          s.substring(18).replaceAll("\\[empty]", "").trim()
+        )
+      )
       .toList();
   }
 
@@ -141,9 +153,15 @@ private String toString(List<int[]> times) {
   }
 
   static List<int[]> toTimes(String list) {
-    var args = list.split("\\|");
+    // The limit is needed to split | into two empty strings
+    // https://errorprone.info/bugpattern/StringSplitter
+    var args = list.split("\\|", -1);
     return Arrays.stream(args)
-      .map(it -> Arrays.stream(it.trim().split(" ")).mapToInt(Integer::parseInt).toArray())
+      .map(it ->
+        it.trim().isEmpty()
+          ? new int[0]
+          : Arrays.stream(it.trim().split(" ")).mapToInt(Integer::parseInt).toArray()
+      )
       .toList();
   }
 }