Switch to dark mode

Errors

Errors occur even in the best of systems, and we always try to provide sensible error codes to help you handle the error properly. All errors that you encounter in the All Aboard API will be GraphQL errors, optionally with an extensions property.

Extensions

Extensions are a GraphQL feature that allows us to add additional information to the error response. This is where we add the error code and any additional information which can help you identify the cause of the error.

json
{
  "errors": [
    {
      "message": "Message describing what went wrong",
      "locations": [{ "line": 6, "column": 7 }],
      "path": ["node", "field", 1, "name"],
      "extensions": {
        "code": "ERROR_CODE",
        "args": {
          "name": "value"
        }
      }
    }
  ]
}

Authentication and authorization

UNAUTHORIZED

The credentials are valid, but they don't allow this operation. This happens when:

  • The API key lacks the base scope for the environment (live or test). Every request with that key is refused, as in the example below. See API Scopes.
  • The key lacks a scope the operation needs, such as orders:read for reading orders. A missing passenger_profiles:read is FORBIDDEN instead.
  • The booking, order or cost center belongs to another sales agent, one you have no access to.
  • passengerProfiles is given a filter.salesAgentUid that names a sales agent you have no access to.
  • An access token is used for an operation outside the ones it allows.
json
{
  "errors": [
    {
      "message": "Provided credentials are not permitted to access the live environment (missing live scope).",
      "extensions": {
        "code": "UNAUTHORIZED"
      }
    }
  ]
}

NOT_AUTHENTICATED

No credentials were sent, or they weren't recognized: the api-key header is missing, the API key is unknown, or the access token has expired or been revoked.

json
{
  "errors": [
    {
      "message": "No or invalid authentication provided! For HTTP requests, use either: (1) `api-key: <your-api-key>` header, or (2) `Authorization: Bearer <jwt-token>` header. For WebSocket connections, provide authentication in the ConnectionInit message payload.",
      "extensions": {
        "code": "NOT_AUTHENTICATED"
      }
    }
  ]
}

USER_TOKEN_MISSING

This happens when a query that requires an end-user identity (the viewer query) is called with only an API key and no user token (JWT). Authenticate the request with a user JWT to resolve the viewer.

json
{
  "errors": [
    {
      "message": "This query requires a user token",
      "extensions": {
        "code": "USER_TOKEN_MISSING"
      }
    }
  ]
}

NOT_AUTHORIZED

This happens when the authenticated user does not have permission to perform the requested action.

json
{
  "errors": [
    {
      "message": "Not authorized",
      "extensions": {
        "code": "NOT_AUTHORIZED"
      }
    }
  ]
}

FORBIDDEN

This happens when:

  • The target sales agent does not allow orders to be created on its behalf: a passenger's cost center belongs to it, or passengerProfiles names it in filter.salesAgentUid. The example below is this case.
  • passengerProfiles is called with a key that lacks the passenger_profiles:read scope. See API Scopes.
json
{
  "errors": [
    {
      "message": "Target agent does not allow orders",
      "extensions": {
        "code": "FORBIDDEN"
      }
    }
  ]
}

Node and resource lookup

NOT_FOUND

This happens when a resource that is required by an operation cannot be found.

json
{
  "errors": [
    {
      "message": "Part was not found",
      "extensions": {
        "code": "NOT_FOUND"
      }
    }
  ]
}

INVALID_ID

This happens when an invalid ID format is provided.

json
{
  "errors": [
    {
      "message": "Invalid order id",
      "extensions": {
        "code": "INVALID_ID"
      }
    }
  ]
}

Validation

VALIDATION_ERROR

When working with Bookings or other inputs, the input data is validated and may fail validation. This error code is used when the input data is invalid. The extensions.validation property will contain the validation rule that failed.

json
{
  "errors": [
    {
      "message": "Passenger date of birth is required",
      "extensions": {
        "code": "VALIDATION_ERROR",
        "validation": "REQUIRED"
      }
    }
  ]
}

Common validation values include:

  • REQUIRED - A required field is missing
  • MISSING_ARGUMENTS - Required arguments are missing

INVALID_ARGUMENT

This happens when an argument refers to a resource that cannot be resolved, such as a cost center that does not exist. passengerProfiles also answers with it for a blank filter.query, a first below 1, a cursor from another search, or a filter.salesAgentUid that is not a company in the profile directory. So does a search or booking whose passengerProfileId isn't a passenger profile's, or where two passengers name the same profile.

json
{
  "errors": [
    {
      "message": "Cost center not found",
      "extensions": {
        "code": "INVALID_ARGUMENT"
      }
    }
  ]
}

Access token errors

These come from createAccessToken. See Access Tokens for what each argument accepts. Minting also returns NOT_FOUND for an order you cannot read, NOT_AUTHORIZED when the minting key cannot itself do what the token is asked for, and WALLET_NOT_AVAILABLE when a WRITE token is asked for on an order whose cost center is configured for wallet payments but has no wallet in the order's currency.

INVALID_RESTRICTIONS

This happens when restrictions names no order. A token is always bound to exactly one order, so orderId has to be supplied.

json
{
  "errors": [
    {
      "message": "restrictions must name an order",
      "extensions": {
        "code": "INVALID_RESTRICTIONS"
      }
    }
  ]
}

INVALID_TTL

This happens when ttlSeconds is outside 1..86400. Omit the argument to get the default of 900 seconds.

json
{
  "errors": [
    {
      "message": "ttlSeconds must be between 1 and 86400",
      "extensions": {
        "code": "INVALID_TTL"
      }
    }
  ]
}

INVALID_CAPABILITIES

This happens when capabilities is given as an empty list. A token that grants nothing is never minted, so an empty list is rejected rather than treated as the default. Omit the argument to get [READ].

json
{
  "errors": [
    {
      "message": "capabilities must grant at least one capability",
      "extensions": {
        "code": "INVALID_CAPABILITIES"
      }
    }
  ]
}

Booking and order errors

BOOKING_NOT_FOUND

This happens when a mutation references a booking id that does not exist, for example a stale id or one taken from a different environment. Request a fresh booking and retry with its id.

json
{
  "errors": [
    {
      "message": "Booking not found",
      "extensions": {
        "code": "BOOKING_NOT_FOUND"
      }
    }
  ]
}

UNKNOWN

This can happen either when creating a booking or when prebooking tickets, which happens when creating an order or a payment. This error code is used when we encounter an upstream error from one of our ticketing providers and the error is not recognized or when the error is not related to the booking or prebooking.

json
{
  "errors": [
    {
      "message": "Unable to process booking",
      "extensions": {
        "code": "UNKNOWN"
      }
    }
  ]
}

NO_TICKETS

Tickets may sell out between the time you select a journey and the time you create a booking. This error code is used when one or more of the selected tickets are no longer available. In that case you should request a new JourneyOffer before trying to create a booking again.

You can use the extensions.args.originName and extensions.args.destinationName to determine which tickets are no longer available.

json
{
  "errors": [
    {
      "message": "One of the selected tickets are no longer available",
      "extensions": {
        "code": "NO_TICKETS",
        "args": {
          "originName": "London",
          "destinationName": "Paris"
        }
      }
    }
  ]
}

COMPARTMENT_TYPE_UNAVAILABLE

The selected compartment type is not available for the selected journey. In that case you should request a new JourneyOffer before trying to create a booking again.

json
{
  "errors": [
    {
      "message": "The selected compartment type is not available",
      "extensions": {
        "code": "COMPARTMENT_TYPE_UNAVAILABLE"
      }
    }
  ]
}

BOOKING_ALREADY_USED

Due to technical limitations with certain ticketing providers, some bookings (depending on the underlying operator) can only be used once. This means that if you try to create an order or a payment which then fails, the booking cannot be reused to perform the same action again. In that case you should create a new booking and try again.

A change to a booking's or a passenger's custom fields once its order is paid or confirmed is refused the same way, with one error per field and its args.key.

json
{
  "errors": [
    {
      "message": "This booking cannot be used again",
      "extensions": {
        "code": "BOOKING_ALREADY_USED"
      }
    }
  ]
}

BOOKING_UNUSABLE

The booking cannot be completed, and retrying it will keep failing: the ticketing provider has left the underlying reservation in a state it can no longer act on. Unlike BOOKING_ALREADY_USED, this is not about reusing a booking that already did its job. This booking never completed and never will. Start a new journey search and create a new booking; reusing the same search results may hand you the same broken reservation.

json
{
  "errors": [
    {
      "message": "This booking can no longer be completed. Please start a new search.",
      "extensions": {
        "code": "BOOKING_UNUSABLE"
      }
    }
  ]
}

PRICE_CHANGED

The price has changed since the selection was made. You should request a new JourneyOffer before trying to create a booking again.

json
{
  "errors": [
    {
      "message": "The price has changed since the selection was made",
      "extensions": {
        "code": "PRICE_CHANGED"
      }
    }
  ]
}

SEAT_UNAVAILABLE

The desired seat is not available for booking.

json
{
  "errors": [
    {
      "message": "The desired seat is not available",
      "extensions": {
        "code": "SEAT_UNAVAILABLE"
      }
    }
  ]
}

NOT_ENOUGH_FUNDS

This happens when finalizing an order with finalizeOrder and the wallet balance does not cover the order total. createOrder does not check the balance. Nothing is booked or charged, so you can top up the wallet and finalize the same order again within the booking's expiration window.

json
{
  "errors": [
    {
      "message": "Not enough funds in wallet",
      "extensions": {
        "code": "NOT_ENOUGH_FUNDS"
      }
    }
  ]
}

ORDER_LOCKED

The order is locked by another operation and cannot be modified at this time.

json
{
  "errors": [
    {
      "message": "Order is locked by other operation.",
      "extensions": {
        "code": "ORDER_LOCKED"
      }
    }
  ]
}

MISSING_CONTACT_PERSON

Exactly one passenger must be marked as the contact person. This error occurs when no passenger has contactPerson set to true.

json
{
  "errors": [
    {
      "message": "One person must be assigned as the contact person",
      "extensions": {
        "code": "MISSING_CONTACT_PERSON"
      }
    }
  ]
}

MULTIPLE_CONTACT_PERSONS

Only one passenger can be the contact person. This error occurs when more than one passenger has contactPerson set to true.

json
{
  "errors": [
    {
      "message": "Only one person can be assigned as the contact person",
      "extensions": {
        "code": "MULTIPLE_CONTACT_PERSONS"
      }
    }
  ]
}

RESERVATION_UNAVAILABLE

A seat reservation for part of the journey could not be confirmed. You should request a new JourneyOffer before trying to create a booking again.

json
{
  "errors": [
    {
      "message": "A seat reservation for part of this journey could not be confirmed",
      "extensions": {
        "code": "RESERVATION_UNAVAILABLE"
      }
    }
  ]
}

SELECTED_OFFER_UNAVAILABLE

A selected offer is no longer available. You should request a new JourneyOffer before trying to create a booking again.

json
{
  "errors": [
    {
      "message": "A selected offer is no longer available. Please try with a new booking.",
      "extensions": {
        "code": "SELECTED_OFFER_UNAVAILABLE"
      }
    }
  ]
}

TICKET_SUPPLIER_FAILURE

The ticket supplier's own booking system rejected or failed the request. This is usually transient, so the same booking can be retried.

json
{
  "errors": [
    {
      "message": "Our ticket supplier failed to complete the booking.",
      "extensions": {
        "code": "TICKET_SUPPLIER_FAILURE"
      }
    }
  ]
}

INVALID_OFFER

One or more of the selected offer ids are invalid or could not be matched to the journey.

json
{
  "errors": [
    {
      "message": "Invalid offer id: ...",
      "extensions": {
        "code": "INVALID_OFFER"
      }
    }
  ]
}

OFFER_SET_INCOMPLETE

Some offers cannot be booked on their own: they belong to a set that spans several segment collections of a journey. A UK return fare, for example, is priced as a whole but appears as an offer on one direction and a companion offer at no extra cost on the other. A journey that is only sold whole shares one set across every offer of every collection. Either way the rule is the same: select an offer from the set for every collection it covers, or nothing from it at all. This error means a collection the set covers is not getting an offer from it.

There are two ways out: complete the set, or drop it. Which one you want depends on what you meant to book. To keep the set, select an offer from it for the collection the message names; on a journey sold whole, that means booking every collection it covers. To drop the set, select an offer outside it for every collection of it you book. That second way out is not always available: where a collection offers nothing outside the set, there is nothing to select in the set's place, so complete the set rather than retrying without it. See Offer Sets.

The message names the collection by where it goes (it calls it a leg) and says what is wrong with it: it is not in the booking at all, it is in the booking with no offer selected, or it is booked with an offer from outside the set. The first of those only reaches you from createBooking; updating a booking's selections cannot leave out a collection the booking already holds.

Use extensions.args.offerSetIds to identify the set, matching it against the memberOf.id of the offers you were shown. The message never names a set id or an offer id; offer ids are rewritten for you, so the id we hold is not the one you hold.

If a request breaks more than one set, you get one error per set rather than only the first. Each error names one collection of its set, so a set missing several is reported one at a time as you fix them; cover every collection the set spans in one call instead.

json
{
  "errors": [
    {
      "message": "This offer set covers the London Euston – Edinburgh leg, which is not in this booking. Select an offer from the set for every leg it covers, or select nothing from it.",
      "extensions": {
        "code": "OFFER_SET_INCOMPLETE",
        "args": {
          "offerSetIds": ["offer_set_01k2yq..."]
        }
      }
    }
  ]
}

When the collection is in the booking but was left without an offer, the message says so instead:

json
{
  "errors": [
    {
      "message": "This offer set covers the Edinburgh – London Euston leg, which has no offer selected. Select an offer from the set for every leg it covers, or select nothing from it.",
      "extensions": {
        "code": "OFFER_SET_INCOMPLETE",
        "args": {
          "offerSetIds": ["offer_set_01k2yq..."]
        }
      }
    }
  ]
}

And when it was given an offer from outside the set:

json
{
  "errors": [
    {
      "message": "This offer set covers the Edinburgh – London Euston leg, which is booked with an offer outside the set. Select an offer from the set for every leg it covers, or select nothing from it.",
      "extensions": {
        "code": "OFFER_SET_INCOMPLETE",
        "args": {
          "offerSetIds": ["offer_set_01k2yq..."]
        }
      }
    }
  ]
}

OFFER_SET_UNAVAILABLE

You selected an offer belonging to a set, but the other parts of the journey that set covers are no longer on offer. The set can no longer be completed, so select an offer outside the set for the collection the message names. Where that collection has nothing else on offer, request a new JourneyOffer before trying to create a booking again.

Only booking creation returns this code; updating a booking's selections does not.

This is not OFFER_SET_INCOMPLETE: there is no missing offer left to select. Use extensions.args.offerSetIds to identify the set, matching it against the memberOf.id of the offers you were shown. You get one error per such set.

json
{
  "errors": [
    {
      "message": "The other legs this offer set covers are no longer on offer, so it cannot be booked. Select an offer outside the set for the London Euston – Edinburgh leg, or search this journey again.",
      "extensions": {
        "code": "OFFER_SET_UNAVAILABLE",
        "args": {
          "offerSetIds": ["offer_set_01k2yq..."]
        }
      }
    }
  ]
}

OFFER_SET_CONFLICT

You selected offers belonging to two different sets that cover the same collection. Neither set can be completed without the other losing a collection, so the combination is not bookable. Select offers from one set only.

extensions.args.offerSetIds holds both sets. They are peers, so the order between them carries no meaning. You get one error per conflicting pair.

json
{
  "errors": [
    {
      "message": "Two offer sets both cover the London Euston – Edinburgh leg and cannot be booked together.",
      "extensions": {
        "code": "OFFER_SET_CONFLICT",
        "args": {
          "offerSetIds": ["offer_set_01k2yq...", "offer_set_01k2yr..."]
        }
      }
    }
  ]
}

NOT_OPTIONAL

This happens when trying to toggle a booking part that is not optional and therefore cannot be added or removed.

json
{
  "errors": [
    {
      "message": "Part is not optional and cannot be toggled",
      "extensions": {
        "code": "NOT_OPTIONAL"
      }
    }
  ]
}

INVALID_ANCILLARY_PASSENGER

The passengerId provided for an ancillary does not match any passenger on the booking.

json
{
  "errors": [
    {
      "message": "Ancillary passenger_id does not match any passenger on the booking",
      "extensions": {
        "code": "INVALID_ANCILLARY_PASSENGER"
      }
    }
  ]
}

INVALID_ANCILLARY_PASSENGER_FOR_OFFER

The ancillary cannot be sold to the given passenger because they are not in the offer's set of applicable passengers.

json
{
  "errors": [
    {
      "message": "Ancillary cannot be sold to passenger — not in the offer's applicable passenger set",
      "extensions": {
        "code": "INVALID_ANCILLARY_PASSENGER_FOR_OFFER"
      }
    }
  ]
}

OFFER_NOT_FOUND

The offer id given to setTicketDelivery matches no offer. The id is an Offer.id, taken from the booking or from the offers you were shown.

json
{
  "errors": [
    {
      "message": "Offer was not found",
      "extensions": {
        "code": "OFFER_NOT_FOUND"
      }
    }
  ]
}

UNAVAILABLE_TICKET_DELIVERY_METHOD

The delivery method given to setTicketDelivery is not one the offer can be issued with. Pick one of the methods in that offer's ticketDelivery.availableOptions. If the offer is the companion of a return fare from a combined journey offer, it has no ticketDelivery. Set the delivery on the full-price offer that shares its memberOf.id instead.

json
{
  "errors": [
    {
      "message": "That ticket delivery method is not available for this offer",
      "extensions": {
        "code": "UNAVAILABLE_TICKET_DELIVERY_METHOD"
      }
    }
  ]
}

TICKET_DELIVERY_CONFLICT

createOrder was refused because offers that the operator delivers together hold different ticket delivery methods. args.offers lists each of those offers with the method it holds. Choose the same method on all of them with setTicketDelivery, then create the order again.

json
{
  "errors": [
    {
      "message": "The offers in this booking that are delivered together have different ticket delivery methods. Choose the same method for all of them.",
      "extensions": {
        "code": "TICKET_DELIVERY_CONFLICT",
        "args": {
          "offers": [
            { "offerId": "offer_01jq3v8k2mfx9t0a7c4r5d6e8h", "method": "E_TICKET" },
            { "offerId": "offer_01jq3v8k2mfx9t0a7c4r5d6e9j", "method": "TICKET_ON_DEPARTURE" }
          ]
        }
      }
    }
  ]
}

PAYMENT_METHOD_NOT_ALLOWED

No payment method is both configured on the cost centers and allowed for the API key used to create the order.

json
{
  "errors": [
    {
      "message": "No payment method is both configured on the cost centers and allowed for this API key",
      "extensions": {
        "code": "PAYMENT_METHOD_NOT_ALLOWED"
      }
    }
  ]
}

WALLET_NOT_AVAILABLE

The cost center is configured for wallet payments but has no wallet in the order's currency.

json
{
  "errors": [
    {
      "message": "The cost center is configured for wallet payments but has no wallet in the order currency",
      "extensions": {
        "code": "WALLET_NOT_AVAILABLE"
      }
    }
  ]
}

FINALIZE_FAILED

finalizeOrder was refused before anything was booked, for example because the payment for the order couldn't be settled. Nothing was charged. Contact us with the order reference if it keeps happening.

json
{
  "errors": [
    {
      "message": "Finalization failed",
      "extensions": {
        "code": "FINALIZE_FAILED"
      }
    }
  ]
}

Finalization errors from operators

When an operator rejects part of an order during finalizeOrder, the mutation returns one error per failed part. Each one carries args.provider and args.orderReference, and one of these codes:

Code Meaning
OFFER_EXPIRED The selected offer expired before the booking could be finalized. Start over with a new search and booking.
BOOKING_EXPIRED The booking expired before it could be finalized. Start over with a new booking.
ALREADY_FINALIZED The operator says the booking is already finalized. Check the order with node before trying anything else.
DUPLICATE_BOOKING The operator rejected the booking as a duplicate of a recent one. Check whether you have already booked the same journey for the same passengers.
UPSTREAM_UNAVAILABLE The operator is temporarily unavailable.
UNKNOWN The operator returned an error we don't recognize.
json
{
  "errors": [
    {
      "message": "The booking expired before it could be finalized",
      "extensions": {
        "code": "BOOKING_EXPIRED",
        "args": {
          "provider": "sj",
          "orderReference": "AAK7Q2M9XP"
        }
      }
    }
  ]
}

ORDER_NOT_PAYABLE

createPayment was called for an order that is already paid, finalized or voided. An order whose earlier payment was canceled or abandoned can still be paid: call createPayment again with the same orderId.

json
{
  "errors": [
    {
      "message": "This order can no longer be paid",
      "extensions": {
        "code": "ORDER_NOT_PAYABLE"
      }
    }
  ]
}

COST_CENTER_NOT_AVAILABLE

A passenger's costCenter isn't one of the cost centers of the sales agent that the first cost center named belongs to: it belongs to another sales agent, or doesn't exist. All passengers of one booking carry cost centers of one agent, so make one booking for each. See Booking for Another Sales Agent.

json
{
  "errors": [
    {
      "message": "This cost center is not available",
      "extensions": {
        "code": "COST_CENTER_NOT_AVAILABLE"
      }
    }
  ]
}

MULTI_COST_CENTER_NOT_SUPPORTED

The passengers in a booking belong to more than one cost center, which isn't enabled for your sales agent. Rail pass bookings never support more than one cost center.

json
{
  "errors": [
    {
      "message": "Multiple cost centers per booking are not supported",
      "extensions": {
        "code": "MULTI_COST_CENTER_NOT_SUPPORTED"
      }
    }
  ]
}

MIXED_COST_CENTER_SPECIFICATION

Some passengers have a non-default costCenter and others have none. When any passenger is assigned to a non-default cost center, every passenger must have one.

json
{
  "errors": [
    {
      "message": "When any passenger specifies a non-default cost center, all passengers must specify one",
      "extensions": {
        "code": "MIXED_COST_CENTER_SPECIFICATION"
      }
    }
  ]
}

MIXED_PAYMENT_METHOD

The cost centers in a booking don't share a payment method. All cost centers in one booking must have at least one payment method in common.

json
{
  "errors": [
    {
      "message": "All cost centers in a booking must share at least one payment method",
      "extensions": {
        "code": "MIXED_PAYMENT_METHOD"
      }
    }
  ]
}

Passenger validation errors

INVALID_AGE

The provided age doesn't match the passenger birth dates.

json
{
  "errors": [
    {
      "message": "The provided age doesn't match the passenger birth dates",
      "extensions": {
        "code": "INVALID_AGE"
      }
    }
  ]
}

AGE_CATEGORY_CHANGED

A passenger's birth date doesn't match the age the offer was priced for, so the passenger now falls into a different passenger type or age category. For tickets this happens when you update passenger details; for rail passes, when you create the order. extensions.args.passengerId identifies the passenger. Price the journey or search for passes again with the right age.

json
{
  "errors": [
    {
      "message": "The birth date does not match the previously selected age",
      "extensions": {
        "code": "AGE_CATEGORY_CHANGED",
        "args": {
          "passengerId": "41c56f83-a3d6-4aba-93c8-9e89681a87da"
        }
      }
    }
  ]
}

PASSENGER_MISMATCH

The passengers you create the booking for aren't the passengers the offers were priced for. They are compared one by one, in the order you send them, on passenger type, age, travel card codes and corporate codes. A card's identifier is not part of the comparison, and the order of a passenger's cards or codes doesn't matter. Price the journey again for the passengers you mean to book, and create the booking from the offers that come back.

json
{
  "errors": [
    {
      "message": "Provided passenger placeholders do not match the passengers the offers were searched for (type, age, travel cards or corporate codes)",
      "extensions": {
        "code": "PASSENGER_MISMATCH"
      }
    }
  ]
}

PASSENGER_FIELD_MISSING

A required passenger field is missing. The extensions.args.field property will contain the name of the missing field and the extensions.args.passengerId can help you identify the passenger.

json
{
  "errors": [
    {
      "message": "Missing passenger field birthDate",
      "extensions": {
        "code": "PASSENGER_FIELD_MISSING",
        "args": {
          "field": "birthDate",
          "passengerId": "a5ce944d-5a44-471c-b1ef-ea90b1400331"
        }
      }
    }
  ]
}

PASSENGER_VALIDATION_FAILED

Passenger validation failed. Additional details may be provided in extensions.args.

json
{
  "errors": [
    {
      "message": "Passenger last name must be between 2 and 15 characters long",
      "extensions": {
        "code": "PASSENGER_VALIDATION_FAILED",
        "args": {
          "validation": "LENGTH",
          "field": "lastName",
          "min": 2,
          "max": 15,
          "passengerId": "90997995-184e-4b38-a1f1-6b8643957a25"
        }
      }
    }
  ]
}

UNKNOWN_PASSENGER_ERROR

An unknown error occurred while resolving passenger placeholders.

json
{
  "errors": [
    {
      "message": "Unknown passenger error",
      "extensions": {
        "code": "UNKNOWN_PASSENGER_ERROR"
      }
    }
  ]
}

DUPLICATE_PASSENGER_NAME

Two or more passengers share the same name, which some ticketing providers do not allow. The duplicated name is available in extensions.args.name.

json
{
  "errors": [
    {
      "message": "There are duplicates in the passenger names: John Doe",
      "extensions": {
        "code": "DUPLICATE_PASSENGER_NAME",
        "args": {
          "name": "John Doe"
        }
      }
    }
  ]
}

TRAVEL_CARD_FIELD_REQUIRED

A travel card attached to a passenger is missing a field that pricing or booking the journey requires, typically the identifier of an Interrail Pass. It can be returned when fetching a journey offer or when creating the booking. Whether it is required depends on the operators and kinds of train in the journey. extensions.args.field names the field. Collect it and create the booking again with the same offer ids; nothing was booked. See Travel Cards.

json
{
  "errors": [
    {
      "message": "Travel card identifier is required.",
      "extensions": {
        "code": "TRAVEL_CARD_FIELD_REQUIRED",
        "args": {
          "field": "identifier"
        }
      }
    }
  ]
}

TRAVEL_CARDS_IMMUTABLE

The travelCards sent for a passenger on updateBooking differ from the ones that passenger already holds. A booking is priced on the cards its passengers hold, so cards are set when pricing the journey offer and creating the booking. They can't be changed afterward. Omit travelCards when updating passengers; sending back the cards the passenger already carries is accepted as a no-op. None of the passenger changes in the call are saved. See Travel Cards.

json
{
  "errors": [
    {
      "message": "Travel cards can't be changed on an existing booking. Search again with the right cards and create a new booking.",
      "extensions": {
        "code": "TRAVEL_CARDS_IMMUTABLE"
      }
    }
  ]
}

CORPORATE_CODES_MIXED

The passengers sent to a journey offer or createBooking don't all carry the same corporateCodes. Every passenger in a search or booking is priced on the same agreements. Search and book passengers on different agreements separately. See Corporate Codes.

json
{
  "errors": [
    {
      "message": "Every passenger must carry the same corporate codes. Book passengers on different corporate agreements separately.",
      "extensions": {
        "code": "CORPORATE_CODES_MIXED"
      }
    }
  ]
}

CORPORATE_CODE_OPERATOR_UNKNOWN

The operator of a corporate code names no operator we know. Send the operator's code as listed on Operator, for example aaop_sj. See Corporate Codes.

json
{
  "errors": [
    {
      "message": "Unknown operator aaop_xyz on a corporate code",
      "extensions": {
        "code": "CORPORATE_CODE_OPERATOR_UNKNOWN"
      }
    }
  ]
}

CORPORATE_CODES_IMMUTABLE

The corporateCodes sent for a passenger on updateBooking differ from the ones that passenger already holds. A booking is priced on its passengers' codes, so they are set when pricing the journey offer and creating the booking and can't be changed afterward. Omit corporateCodes when updating passengers; sending back the codes the passenger already carries is accepted as a no-op. None of the passenger changes in the call are saved. See Corporate Codes.

json
{
  "errors": [
    {
      "message": "Corporate codes can't be changed on an existing booking. Search again with the right codes and create a new booking.",
      "extensions": {
        "code": "CORPORATE_CODES_IMMUTABLE"
      }
    }
  ]
}

Rail pass errors

INVALID_COUNTRY_FOR_PASS

The selected pass is not valid in the passenger's country of residence.

json
{
  "errors": [
    {
      "message": "The selected type of pass is not valid in the passenger's country of residence",
      "extensions": {
        "code": "INVALID_COUNTRY_FOR_PASS"
      }
    }
  ]
}

RESIDENCE_MATCHES_PASS_COUNTRY

A One Country Pass cannot be used by a passenger whose country of residence is the same as the pass's country. Choose a Global Pass or a pass for a different country.

json
{
  "errors": [
    {
      "message": "A One Country Pass can't be used in the passenger's own country of residence. Choose a Global Pass or a pass for a different country.",
      "extensions": {
        "code": "RESIDENCE_MATCHES_PASS_COUNTRY"
      }
    }
  ]
}

NO_COUNTRY_PROVIDED

No country of residence was provided for a passenger.

json
{
  "errors": [
    {
      "message": "No country of residence was provided",
      "extensions": {
        "code": "NO_COUNTRY_PROVIDED"
      }
    }
  ]
}

CAMPAIGN_CLOSED

The selected campaign deal is no longer available.

json
{
  "errors": [
    {
      "message": "The selected campaign deal is no longer available",
      "extensions": {
        "code": "CAMPAIGN_CLOSED"
      }
    }
  ]
}

CAMPAIGN_NOT_FOUND

The campaign with the given ID does not exist.

json
{
  "errors": [
    {
      "message": "The campaign with the given id does not exist",
      "extensions": {
        "code": "CAMPAIGN_NOT_FOUND"
      }
    }
  ]
}

PASS_UNAVAILABLE

The selected pass is no longer available.

json
{
  "errors": [
    {
      "message": "The selected pass is not available",
      "extensions": {
        "code": "PASS_UNAVAILABLE"
      }
    }
  ]
}

Journey search errors

PLACEHOLDER_PASS_CODES_UNAVAILABLE

Tickets for the requested route could not be looked up at the moment. You should try again later.

json
{
  "errors": [
    {
      "message": "Unable to look up tickets for this route at the moment. Please try again later.",
      "extensions": {
        "code": "PLACEHOLDER_PASS_CODES_UNAVAILABLE"
      }
    }
  ]
}

Passenger profile errors

NO_PROFILE_DIRECTORY

Your sales agent keeps no profile directory, so passengerProfiles has nothing to search. Retrying won't change that; contact team@allaboard.eu if your sales agent should have one. The signed-in user's me.profile is null in the same case rather than an error. A search or booking that names a passenger profile gets the same error.

json
{
  "errors": [
    {
      "message": "The sales agent has no profile directory to search",
      "path": ["passengerProfiles"],
      "extensions": {
        "code": "NO_PROFILE_DIRECTORY"
      }
    }
  ]
}

PASSENGER_PROFILE_NOT_FOUND

A search or booking names a passenger profile that doesn't exist, or one you may not book for. The two aren't told apart.

json
{
  "errors": [
    {
      "message": "Passenger profile not found",
      "extensions": {
        "code": "PASSENGER_PROFILE_NOT_FOUND"
      }
    }
  ]
}

CUSTOM_FIELD_UNKNOWN

A custom field change names a key the booking or passenger has no field for. Fields can't be added, only the values of the ones there changed. Like every custom field refusal, it comes once per field, with args.key and, for a passenger's field, args.passengerId, and saves nothing.

json
{
  "errors": [
    {
      "message": "There is no custom field 9b2d…. Only the value of a custom field that is there can be changed.",
      "path": ["updateBooking"],
      "extensions": {
        "code": "CUSTOM_FIELD_UNKNOWN",
        "args": { "passengerId": "…", "key": "9b2d…" }
      }
    }
  ]
}

CUSTOM_FIELD_READ_ONLY

The custom field is readonly for you, so its value can't be changed. Sending back the value it already holds is not an error.

json
{
  "errors": [
    {
      "message": "The custom field 9b2d… is read-only for you.",
      "path": ["setCustomFields"],
      "extensions": {
        "code": "CUSTOM_FIELD_READ_ONLY",
        "args": { "key": "9b2d…" }
      }
    }
  ]
}

CUSTOM_FIELD_VALUE_INVALID

The value doesn't fit the custom field's type: send a Boolean for BOOLEAN, a YYYY-MM-DD date for DATE, and text otherwise.

json
{
  "errors": [
    {
      "message": "The value of custom field 9b2d… does not fit its type: a boolean for BOOLEAN, a date (YYYY-MM-DD) for DATE, and text otherwise.",
      "path": ["setCustomFields"],
      "extensions": {
        "code": "CUSTOM_FIELD_VALUE_INVALID",
        "args": { "key": "9b2d…" }
      }
    }
  ]
}

CUSTOM_FIELD_DUPLICATE

The same custom field key is sent more than once in one change.

json
{
  "errors": [
    {
      "message": "The custom field 9b2d… is named more than once.",
      "path": ["setCustomFields"],
      "extensions": {
        "code": "CUSTOM_FIELD_DUPLICATE",
        "args": { "key": "9b2d…" }
      }
    }
  ]
}

CUSTOM_FIELD_REQUIRED

createOrder is refused while a required custom field that you can fill is empty, on the booking or on a passenger. There is one error per empty field, with args.key, args.name and, for a passenger's field, args.passengerId. Fill them in and create the order again.

json
{
  "errors": [
    {
      "message": "The custom field Cost Center is required",
      "path": ["createOrder"],
      "extensions": {
        "code": "CUSTOM_FIELD_REQUIRED",
        "args": { "key": "9b2d…", "name": "Cost Center", "passengerId": "…" }
      }
    }
  ]
}

Timetable errors

OUTSIDE_TIMETABLE

Just like with ticket availability, operators release new timetables at different intervals. Most often this happens around the end of the year when both booking horizons and timetable availability may be shorter than usual.

json
{
  "errors": [
    {
      "message": "The timetable for the given date is not available yet",
      "extensions": {
        "code": "OUTSIDE_TIMETABLE"
      }
    }
  ]
}

DATE_IN_PAST

The search date is in the past.

json
{
  "errors": [
    {
      "message": "Search date is in the past",
      "extensions": {
        "code": "DATE_IN_PAST"
      }
    }
  ]
}

LOCATIONS_EQUAL

Equal origin and destination locations were provided.

json
{
  "errors": [
    {
      "message": "Equal origin and destination given",
      "extensions": {
        "code": "LOCATIONS_EQUAL"
      }
    }
  ]
}

LOCATION_NOT_FOUND

A location with the given UID could not be resolved.

json
{
  "errors": [
    {
      "message": "Could not resolve location e:...",
      "extensions": {
        "code": "LOCATION_NOT_FOUND"
      }
    }
  ]
}

RATE_LIMITED

The request has been rate limited. Journey searches and timetable lookups are limited per sales agent, and so is finalizeOrder (20 orders per minute by default, counting failed attempts). For finalizations the message is "Too many orders finalized in a short time, retry later."; nothing was booked or charged, so queue your finalizations and retry the same order. When pricing with journeyOffer, a rate-limited leg is not an error: its status is RATE_LIMITED. See the rateLimits field of SalesAgent for your current limits.

json
{
  "errors": [
    {
      "message": "You have been rate limited.",
      "extensions": {
        "code": "RATE_LIMITED"
      }
    }
  ]
}

Refund errors

ALREADY_REFUNDED

The order is already refunded.

json
{
  "errors": [
    {
      "message": "Order is already refunded.",
      "extensions": {
        "code": "ALREADY_REFUNDED"
      }
    }
  ]
}

REFUND_ALREADY_IN_PROGRESS

A refund is already in progress for this order.

json
{
  "errors": [
    {
      "message": "A refund is already in progress.",
      "extensions": {
        "code": "REFUND_ALREADY_IN_PROGRESS"
      }
    }
  ]
}

REFUND_EXPIRED

The refund has expired.

json
{
  "errors": [
    {
      "message": "Refund has expired.",
      "extensions": {
        "code": "REFUND_EXPIRED"
      }
    }
  ]
}

REFUND_INVALID

The refund is invalid.

json
{
  "errors": [
    {
      "message": "Refund is invalid.",
      "extensions": {
        "code": "REFUND_INVALID"
      }
    }
  ]
}

UNEXPECTED_REFUND_STATE

The refund is in an unexpected state and cannot be processed.

json
{
  "errors": [
    {
      "message": "Refund is in an unexpected state",
      "extensions": {
        "code": "UNEXPECTED_REFUND_STATE"
      }
    }
  ]
}

NO_ITEMS_SELECTED

No items were selected for the refund.

json
{
  "errors": [
    {
      "message": "No items selected for refund",
      "extensions": {
        "code": "NO_ITEMS_SELECTED"
      }
    }
  ]
}

WALLET_CREDIT_FAILED

The refund was processed successfully but the wallet could not be credited. You should contact support.

json
{
  "errors": [
    {
      "message": "Refund was processed successfully but failed to credit the wallet. Please contact support.",
      "extensions": {
        "code": "WALLET_CREDIT_FAILED"
      }
    }
  ]
}

Other errors

NO_MATCH

Unable to find a matching trip.

json
{
  "errors": [
    {
      "message": "Unable to find a matching trip",
      "extensions": {
        "code": "NO_MATCH"
      }
    }
  ]
}

VALIDATION_FAILED

Order validation failed. This is a general validation error that may occur during order creation.

json
{
  "errors": [
    {
      "message": "Order validation failed",
      "extensions": {
        "code": "VALIDATION_FAILED"
      }
    }
  ]
}

UPSTREAM_UNAVAILABLE

A service the request depends on could not be reached or did not answer. Nothing in the request needs correcting, so try again later. The service can be:

  • An operator, when finalizeOrder books a part of the order. The error carries args.provider and args.orderReference; see Finalization errors from operators.
  • The profile directory, when passengerProfiles could not search it, as in the example below, or when a search or booking could not read a passenger profile it names or its custom fields. The signed-in user's me.profile is null in that case rather than an error.
  • A service behind the API itself, on any request. The error then carries args.status or args.closeCode.
json
{
  "errors": [
    {
      "message": "The profile directory could not be searched",
      "path": ["passengerProfiles"],
      "extensions": {
        "code": "UPSTREAM_UNAVAILABLE"
      }
    }
  ]
}

INTERNAL_ERROR

Something failed on our side. The failure has been reported to us, so there is nothing to correct in the request; retrying it later is the only useful response. Quoting args.requestId in a support request lets us find the exact failure. It is null for operations sent over a WebSocket, which have no request id.

The message is always the same fixed sentence and never describes the underlying failure.

json
{
  "errors": [
    {
      "message": "Something went wrong on our side. Please try again in a moment.",
      "extensions": {
        "code": "INTERNAL_ERROR",
        "args": {
          "requestId": "F9-8OGZUbvcJmDsAAAmB"
        }
      }
    }
  ]
}

TIMEOUT

An operation waited too long for something behind it (usually a rail provider) and gave up. Whether the work it started completed anyway is unknown, so re-read the booking or order before retrying rather than assuming nothing happened. As with INTERNAL_ERROR, quote args.requestId in a support request; it is null over a WebSocket.

json
{
  "errors": [
    {
      "message": "The request timed out. Please try again.",
      "extensions": {
        "code": "TIMEOUT",
        "args": {
          "requestId": "F9-8OGZUbvcJmDsAAAmB"
        }
      }
    }
  ]
}