-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Description
The application currently experiences a fatal crash during the startup process when parsing a GTFS feed that includes transfers.txt
entries with a transfer_type
of 4 or 5. These transfer types, while part of the official GTFS specification, are not yet implemented in the TransferType
enum, leading to an IllegalArgumentException
.
This issue was identified when using the Swiss GTFS data, which utilizes these transfer types.
Reference
According to the official GTFS documentation, the definitions for these transfer types are:
- 4 - In-seat transfer: Passengers can transfer from one trip to another by staying on board the same vehicle.
- 5 - In-seat transfer not allowed: Passengers must alight from the vehicle and re-board to transfer between sequential trips.
Problem
The current implementation of the TransferType
enum only supports types 0 through 3. When the parser encounters a value of 4 or 5, it is unable to map it to an existing enum constant and throws an exception, which in turn prevents the application from starting.
package org.naviqore.gtfs.schedule.type;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* Defines the types of transfers between routes as specified in the GTFS feed standards.
* <p>
* For more information on transfer types, see <a
* href="https://support.google.com/transitpartners/answer/6377424?hl=en">GTFS Transfers</a>.
*
* @author munterfi
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
public enum TransferType {
RECOMMENDED(0, "Recommended transfer point between two routes."),
TIMED(1, "Timed transfer between two routes. The departing vehicle is expected to wait for the arriving one."),
MINIMUM_TIME(2, "Transfer requires a minimum amount of time between arrival and departure to ensure a connection."),
NOT_POSSIBLE(3, "Transfer is not possible between routes at this location.");
private final int code;
private final String description;
public static TransferType parse(String code) {
return parse(Integer.parseInt(code));
}
public static TransferType parse(int code) {
for (TransferType type : TransferType.values()) {
if (type.code == code) {
return type;
}
}
throw new IllegalArgumentException("No transfer type with code " + code + " found");
}
}
Proposed Solution
To resolve this, the TransferType
enum should be updated to include the IN_SEAT_TRANSFER
(4) and IN_SEAT_TRANSFER_NOT_ALLOWED
(5) types. Additionally, the application logic that handles transfers will need to be reviewed and potentially adjusted to correctly interpret and utilize these new transfer types.
Reproducible Example
❯ docker run -p 8080:8080 -e GTFS_STATIC_URI=https://data.opentransportdata.swiss/dataset/timetable-2025-gtfs2020/permalink ghcr.io/naviqore/public-transit-service:latest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.4.5)
2025-08-14T15:15:23.848Z INFO 1 --- [ main] org.naviqore.app.Application : Starting Application using Java 21 with PID 1 (/app/app.jar started by root in /app)
2025-08-14T15:15:23.849Z INFO 1 --- [ main] org.naviqore.app.Application : No active profile set, falling back to 1 default profile: "default"
2025-08-14T15:15:25.041Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2025-08-14T15:15:25.054Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-08-14T15:15:25.054Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.40]
2025-08-14T15:15:25.079Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-08-14T15:15:25.080Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1181 ms
2025-08-14T15:15:25.263Z INFO 1 --- [ main] o.n.a.s.PublicTransitSpringService : Initializing public transit spring service
2025-08-14T15:15:25.428Z INFO 1 --- [ main] o.naviqore.utils.network.FileDownloader : Downloading file: https://data.opentransportdata.swiss/dataset/timetable-2025-gtfs2020/permalink
2025-08-14T15:15:41.274Z INFO 1 --- [ main] o.naviqore.utils.network.FileDownloader : Dataset downloaded successfully to: /tmp/tmp_gtfs_2065902117131781254/gtfs.zip
2025-08-14T15:15:41.288Z INFO 1 --- [ main] o.n.gtfs.schedule.GtfsScheduleReader : Reading GTFS from ZIP file: /tmp/tmp_gtfs_2065902117131781254/gtfs.zip
2025-08-14T15:15:41.289Z INFO 1 --- [ main] o.n.gtfs.schedule.GtfsScheduleReader : Reading GTFS file from ZIP: agency.txt
2025-08-14T15:15:41.320Z INFO 1 --- [ main] o.n.gtfs.schedule.GtfsScheduleReader : Reading GTFS file from ZIP: calendar.txt
2025-08-14T15:15:41.519Z INFO 1 --- [ main] o.n.gtfs.schedule.GtfsScheduleReader : Reading GTFS file from ZIP: calendar_dates.txt
2025-08-14T15:15:49.689Z INFO 1 --- [ main] o.n.gtfs.schedule.GtfsScheduleReader : Reading GTFS file from ZIP: stops.txt
2025-08-14T15:15:49.923Z INFO 1 --- [ main] o.n.gtfs.schedule.GtfsScheduleReader : Reading GTFS file from ZIP: routes.txt
2025-08-14T15:15:49.935Z INFO 1 --- [ main] o.n.gtfs.schedule.GtfsScheduleReader : Reading GTFS file from ZIP: trips.txt
2025-08-14T15:15:52.840Z INFO 1 --- [ main] o.n.gtfs.schedule.GtfsScheduleReader : Reading GTFS file from ZIP: stop_times.txt
2025-08-14T15:16:26.067Z INFO 1 --- [ main] o.n.gtfs.schedule.GtfsScheduleReader : Reading GTFS file from ZIP: transfers.txt
2025-08-14T15:16:26.105Z WARN 1 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'routingController' defined in URL [jar:nested:/app/app.jar/!BOOT-INF/classes/!/org/naviqore/app/controller/RoutingController.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'publicTransitSpringService' defined in URL [jar:nested:/app/app.jar/!BOOT-INF/classes/!/org/naviqore/app/service/PublicTransitSpringService.class]: Failed to instantiate [org.naviqore.app.service.PublicTransitSpringService]: Constructor threw exception
2025-08-14T15:16:26.109Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2025-08-14T15:16:26.125Z INFO 1 --- [ main] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2025-08-14T15:16:26.144Z ERROR 1 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'routingController' defined in URL [jar:nested:/app/app.jar/!BOOT-INF/classes/!/org/naviqore/app/controller/RoutingController.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'publicTransitSpringService' defined in URL [jar:nested:/app/app.jar/!BOOT-INF/classes/!/org/naviqore/app/service/PublicTransitSpringService.class]: Failed to instantiate [org.naviqore.app.service.PublicTransitSpringService]: Constructor threw exception
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1387) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1224) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:371) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1221) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1187) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1122) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) ~[spring-context-6.2.6.jar!/:6.2.6]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) ~[spring-context-6.2.6.jar!/:6.2.6]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.4.5.jar!/:3.4.5]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) ~[spring-boot-3.4.5.jar!/:3.4.5]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-3.4.5.jar!/:3.4.5]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) ~[spring-boot-3.4.5.jar!/:3.4.5]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1362) ~[spring-boot-3.4.5.jar!/:3.4.5]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1351) ~[spring-boot-3.4.5.jar!/:3.4.5]
at org.naviqore.app.Application.main(Application.java:12) ~[!/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:102) ~[app.jar:na]
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:64) ~[app.jar:na]
at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:40) ~[app.jar:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'publicTransitSpringService' defined in URL [jar:nested:/app/app.jar/!BOOT-INF/classes/!/org/naviqore/app/service/PublicTransitSpringService.class]: Failed to instantiate [org.naviqore.app.service.PublicTransitSpringService]: Constructor threw exception
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:321) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:309) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1387) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1224) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:371) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1739) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1627) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) ~[spring-beans-6.2.6.jar!/:6.2.6]
... 26 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.naviqore.app.service.PublicTransitSpringService]: Constructor threw exception
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:222) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:145) ~[spring-beans-6.2.6.jar!/:6.2.6]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:318) ~[spring-beans-6.2.6.jar!/:6.2.6]
... 40 common frames omitted
Caused by: java.lang.IllegalArgumentException: No transfer type with code 4 found
at org.naviqore.gtfs.schedule.type.TransferType.parse(TransferType.java:36) ~[naviqore-gtfs-2.1.0.jar!/:na]
at org.naviqore.gtfs.schedule.type.TransferType.parse(TransferType.java:27) ~[naviqore-gtfs-2.1.0.jar!/:na]
at org.naviqore.gtfs.schedule.GtfsScheduleParser.parseTransfers(GtfsScheduleParser.java:116) ~[naviqore-gtfs-2.1.0.jar!/:na]
at org.naviqore.gtfs.schedule.GtfsScheduleParser.parse(GtfsScheduleParser.java:40) ~[naviqore-gtfs-2.1.0.jar!/:na]
at org.naviqore.gtfs.schedule.GtfsScheduleReader.lambda$readCsvRecords$0(GtfsScheduleReader.java:110) ~[naviqore-gtfs-2.1.0.jar!/:na]
at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
at org.naviqore.gtfs.schedule.GtfsScheduleReader.readCsvRecords(GtfsScheduleReader.java:110) ~[naviqore-gtfs-2.1.0.jar!/:na]
at org.naviqore.gtfs.schedule.GtfsScheduleReader.readFromZip(GtfsScheduleReader.java:70) ~[naviqore-gtfs-2.1.0.jar!/:na]
at org.naviqore.gtfs.schedule.GtfsScheduleReader.read(GtfsScheduleReader.java:139) ~[naviqore-gtfs-2.1.0.jar!/:na]
at org.naviqore.app.infrastructure.GtfsScheduleUrl.get(GtfsScheduleUrl.java:27) ~[!/:na]
at org.naviqore.service.gtfs.raptor.GtfsRaptorServiceInitializer.<init>(GtfsRaptorServiceInitializer.java:40) ~[naviqore-public-transit-service-2.1.0.jar!/:na]
at org.naviqore.service.PublicTransitServiceFactory.create(PublicTransitServiceFactory.java:15) ~[naviqore-public-transit-service-2.1.0.jar!/:na]
at org.naviqore.app.service.PublicTransitSpringService.createDelegate(PublicTransitSpringService.java:49) ~[!/:na]
at org.naviqore.app.service.PublicTransitSpringService.<init>(PublicTransitSpringService.java:32) ~[!/:na]
at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486) ~[na:na]
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:209) ~[spring-beans-6.2.6.jar!/:6.2.6]
... 42 common frames omitted