Understanding OpenTripPlanner API error responses
When making requests to OpenTripPlanner (OTP), an open-source, multi-modal trip planner, things don't always work out and you may find yourself facing an error response.
This article provides a quick overview of such responses, on both versions of OTP.
Response format
OpenTripPlanner uses different data formats (namely XML and JSON) depending on its version, and slighlty different structures too.
OTP 1 (XML)
In it's first (= previous) version, OpenTripPlanner's response format uses XML, and it's quite consistent whether the request was successful or not. In both cases, it contains (among other things) an error
property that is either empty when there's no error, or provides details on what went wrong when applicable.
The response looks like this:
<Response>
<!-- List of all parameters given in the request. -->
<requestParameters/>
<!-- On success: the trip plan with all possible itineraries. -->
<!-- On failure: empty. -->
<!-- See http://dev.opentripplanner.org/apidoc/1.5.0/json_TripPlan.html -->
<plan/>
<!-- On success: empty. -->
<!-- On failure: description of the error. -->
<!-- See http://dev.opentripplanner.org/apidoc/1.5.0/json_PlannerError.html -->
<error>
<id>406</id>
<msg>No transit times available. The date may be past or too far in the future or there may not be transit service for your trip at the time you chose.</msg>
<message>NO_TRANSIT_TIMES</message>
<noPath>true</noPath>
</error>
<!-- Information for debugging and profiling purposes. -->
<!-- See http://dev.opentripplanner.org/apidoc/1.5.0/json_DebugOutput.html -->
<debugOutput/>
<!-- Metadata about elevation. -->
<!-- See http://dev.opentripplanner.org/apidoc/1.5.0/json_ElevationMetadata.html -->
<elevationMetadata/>
</Response>
OTP 2 (JSON)
In it's second (= latest) version, OTP's response uses JSON and is a little bit more different depending on whether the request succeeded or failed. Among other things, the error
property only exists when an error has in fact occured.
The response looks like this:
{
// List of all parameters given in the request.
"requestParameters": {},
"plan": {
"date": 0,
"from": {},
"to": {},
// On success: the list of all possible itineraries.
// On failure: empty.
"itineraries": []
},
// On success: non-existent.
// On failure: description of the error.
"error": {
"id": 440,
"msg": "Origin is unknown. Can you be a bit more descriptive?",
"message": "GEOCODE_FROM_NOT_FOUND",
"missing": ["FROM_PLACE"]
},
// On success: Contextual information about the response.
// On failure: non-existent.
"metadata": {},
// On success: a value that can be used in a subsequent request's "pageCursor" parameter to fetch the previous/next page of itineraries.
// On failure: non-existent.
"previousPageCursor": "string",
"nextPageCursor": "string",
// Information for debugging and profiling purposes.
// See https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/types/debugOutput
"debugOutput": {},
}
You may have noticed that the error object is comparable in both versions. Let's have a closer look at it.
The error object
OpenTripPlanner's error responses always provide an error
object that contains the following elements:
id
(required)
This 3-digit integer looks remarkably like an HTTP status code, but it's not.
Even though there's significant overlap between these ids and status code (e.g. the PATH_NOT_FOUND
message uses the 404
id), you'll also find a bunch of ids that don't match a status code (e.g. 373
, 470
, etc.).
Furthermore, note that the HTTP code actually returned in the response's headers is always 200
, even when an error occured and no itineraries were found, regardless of the error's pseudo-http-code-id value. This is a known discrepancy or confusion point (whatever you want to call it) as you can see in this GitHub issue.
So in short, the id
property is just that: a unique, numerical identifier.
msg
(required)
This is a human-readable explanation of the error encountered. When you're debugging a failed request, this is usually the clearest and most helpful piece of information. It's also fit for display to the end users of your planner.
The msg
value is available in multiple languages in the Message_[languageCode].properties
source files for OTP 1 and OTP 2.
message
(required)
This is a short, machine-readable key to identify each error. It fulfils the same role as the error's id, but it's a lot more explicit. Therefore it's recommended to use this in scripts (e.g. if (code === "NO_TRANSIT_TIMES")
) instead of the id (e.g. if (id === 406)
).
It's a bit weird that OpenTripPlanner developers chose the msg
property name for the longer, human-friendly message, and message
for the shorter, machine-reable identifier. But there you have it.
noPath
(required, OTP 1 only)
This is a boolean flag only available on OTP 1. It indicates whether OTP was unable to find any itinerary or "path".
I haven't tested it fully, but I'm fairly certain OTP 1 returns <noPath>true</noPath>
in every error object. This is completely redundant, which I suppose is why this property no longer exists in OTP 2.
missing
(conditionally required)
This last property only appears on errors of type VertexNotFoundException
, i.e. when trying to use a location that doesn't exist in the planner's graph. This includes the message=GEOCODE_FROM_NOT_FOUND
error for example.
Full list of error messages
Here are all the errors you can encounter in either version of the OpenTripPlanner API.
id | message | msg |
---|---|---|
340 | GEOCODE_FROM_AMBIGUOUS | The trip planner is unsure of the location you want to start from. Please select from the following options, or be more specific. |
350 | GEOCODE_TO_AMBIGUOUS | The trip planner is unsure of the destination you want to go to. Please select from the following options, or be more specific. |
360 | GEOCODE_FROM_TO_AMBIGUOUS | Both origin and destination are ambiguous. Please select from the following options, or be more specific. |
370 | UNDERSPECIFIED_TRIANGLE | All of triangleSafetyFactor, triangleSlopeFactor, and triangleTimeFactor must be set if any are |
371 | TRIANGLE_NOT_AFFINE | The values of triangleSafetyFactor, triangleSlopeFactor, and triangleTimeFactor must sum to 1 |
372 | TRIANGLE_OPTIMIZE_TYPE_NOT_SET | If triangleSafetyFactor, triangleSlopeFactor, and triangleTimeFactor are provided, OptimizeType must be TRIANGLE |
373 | TRIANGLE_VALUES_NOT_SET | If OptimizeType is TRIANGLE, triangleSafetyFactor, triangleSlopeFactor, and triangleTimeFactor must be set |
400 | OUTSIDE_BOUNDS | Trip is not possible. You might be trying to plan a trip outside the map data boundary. |
404 | PATH_NOT_FOUND | No trip found. There may be no transit service within the maximum specified distance or at the specified time, or your start or end point might not be safely accessible. |
406 | NO_TRANSIT_TIMES | No transit times available. The date may be past or too far in the future or there may not be transit service for your trip at the time you chose. |
408 | REQUEST_TIMEOUT | The trip planner is taking way too long to process your request. Please try again later. |
409 | TOO_CLOSE | Origin is within a trivial distance of the destination. |
413 | BOGUS_PARAMETER | "The request has errors that the server is not willing or able to process.” |
440 | GEOCODE_FROM_NOT_FOUND | Origin is unknown. Can you be a bit more descriptive? |
450 | GEOCODE_TO_NOT_FOUND | Destination is unknown. Can you be a bit more descriptive? |
460 | GEOCODE_FROM_TO_NOT_FOUND | Both origin and destination are unknown. Can you be a bit more descriptive? |
470 | LOCATION_NOT_ACCESSIBLE | The location was found, but no stops could be found within the search radius. |
500 | SYSTEM_ERROR | We're sorry. The trip planner is temporarily unavailable. Please try again later. |
503 | GRAPH_UNAVAILABLE |