Error handling
A GraphQL query is an HTTP POST request with the content type application/json
and the body having the form:
{
"query" : "...",
"variables" : "..."
}
If the authorization for a request fails (no authorization header, expired token, etc.) the return status code is 401
(Unauthorized)
and the response body is the following:
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
However, for most requests, whether they are successful or they failed, the return status code is 200
. When a successful query is executed, the JSON object that is returned contains a property called data
whose value is an object representing the returned data. Here is an example:
{
"data": {
"useCompany": {
"generalLedgerAccount": {
"items": [
{
"accountNo": 9001,
"name": "Special account",
}
]
}
}
}
}
When an error occurs during the execution of the request, the return JSON object contains both the data
property, as well as a property called errors
, which is an array of objects containing information about the error(s) that occurred. The following is an example:
{
"errors": [
{
"message": "Error: Could not get authorize user using VismaNetCompanyId: 1234567. Description: External integration error. Status: 18."
}
],
"data": {
"useCompany": {
"generalLedgerAccount": null
}
}
}
If the GraphQL query has a syntax error, the returned information contains not just a message but also detains about the location of the error within the query. This is exemplified below:
query read($cid : Int!)
{
useCompany(no : $cid) {
generalLedgerAccount {
totalRows
}
}
}
{
"errors": [
{
"message": "GraphQL.Validation.Errors.FieldsOnCorrectTypeError: Cannot query field 'totalRows' on type 'Query_UseCompany_GeneralLedgerAccount_Connection'. Did you mean 'totalCount'?",
"locations": [
{
"line": 4,
"column": 7
}
],
"extensions": {
"code": "FIELDS_ON_CORRECT_TYPE",
"codes": [
"FIELDS_ON_CORRECT_TYPE"
],
"number": "5.3.1"
}
}
]
}
A GraphQL query may contain multiple requests (such as reading from different tables). It is possible that some may be successful, while other will fail. The result will contain data that was fetched successfully but also information about the errors that occurred for the other requests. Here is an example:
query read($cid : Int!)
{
useCompany(no : $cid) {
generalLedgerAccount(first: 2) {
totalCount
items {
accountNo
name
}
}
generalLedgerBalance(last: 10) {
totalCount
items {
accountNo
sumDebitObDomestic
sumCreditObDomestic
}
}
}
}
{
"errors": [
{
"message": "The cursor 'before' must have a value.",
"path": [
"useCompany",
"generalLedgerBalance"
]
}
],
"data": {
"useCompany": {
"generalLedgerAccount": {
"totalCount": 340,
"items": [
{
"accountNo": 1000,
"name": "Forskning og utvikling"
},
{
"accountNo": 1020,
"name": "Konsesjoner"
}
]
},
"generalLedgerBalance": null
}
}
}
On the other hand, an operation may be successful (such as inserting a new row) although sub-operations (such as assigning a value to a column) may fail. GraphQL returns the result as well as all the errors messages from executing the request.
mutation create_batch($cid : Int!)
{
useCompany(no: $cid)
{
batch_create(values:
[
{
valueDate: 20211122
voucherSeriesNo: 3
orgUnit1 : 0
orgUnit2 : 0
orgUnit3 : 0
orgUnit4 : 0
orgUnit5 : 0
period :11
year :2021
}
])
{
affectedRows
items
{
batchNo
valueDate
voucherSeriesNo
}
}
}
}
{
"errors": [
{
"message": "Error: Illegal value date 11/22/2021. Check suspension date and the accounting periods and VAT periods tables..",
"path": [
"useCompany",
"batch_create",
"values/0"
],
"extensions": {
"data": {
"status": 0
}
}
},
{
"message": "Error: Org unit class not named. Description: Not read access to destination column. Column: OrgUnit3.",
"path": [
"useCompany",
"batch_create",
"values/0"
],
"extensions": {
"data": {
"status": 3,
"status_name" : "NotReadAccessToDestinationColumn"
}
}
},
{
"message": "Error: Org unit class not named. Description: Not read access to destination column. Column: OrgUnit4.",
"path": [
"useCompany",
"batch_create",
"values/0"
],
"extensions": {
"data": {
"status": 3,
"status_name" : "NotReadAccessToDestinationColumn"
}
}
},
{
"message": "Error: Org unit class not named. Description: Not read access to destination column. Column: OrgUnit5.",
"path": [
"useCompany",
"batch_create",
"values/0"
],
"extensions": {
"data": {
"status": 3,
"status_name" : "NotReadAccessToDestinationColumn"
}
}
}
],
"data": {
"useCompany": {
"batch_create": {
"affectedRows": 1,
"items": [
{
"batchNo": 26,
"valueDate": 20211122,
"voucherSeriesNo": 3
}
]
}
}
}
}
Understanding error information
When an error occurs during the execution of the query, you may see the following information for each returned error:
message
: always present, contains a description of the errorpath
: contains the path in the query (schema) of the field that produced the errorextensions
: additional information about the error. An example isdata:status
that contains an internal error code that could be useful for troubleshouting a failed execution. In addition,data:status_name
provides a symbolic name of the status code, such asNotReadAccessToDestinationColumn
.
Another example of an error message from attempting to create an order with a duplicate ID is shown below. You can see that status
is 3 but status_name
is set to PrimaryKeyAssignmentFailed
to give you a better understanding of what the status code 3 means in this context.
{
"errors": [
{
"message": "A record with the same primary key already exists.",
"path": [
"useCompany",
"order_create",
"values/0"
],
"extensions": {
"data": {
"status": 3,
"status_name": "PrimaryKeyAssignmentFailed"
}
}
}
],
"data": {
"useCompany": {
"order_create": {
"items": null
}
}
}
}
However, it is important to note that these status codes (and their names) are not unique. A request is composed of multiple operations, such as selecting a table, assigning a value to a field, etc. There are various status codes for each such contextual operation.
Therefore, an operation may return status code 3 that means NotReadAccessToDestinationColumn
, or status code 3 that means NotInsertAccessToTable
.
That is why the status_name
field is useful to help you better understand the problem.
Tip
For more information about the GraphQL engine errors (such as schema errors, input errors and processing errors) see GraphQL.NET Error Handling.
Tracing query execution
Each GraphQL query that executes is assigned a unique identifier. This is used to trace the execution of the query and can be used for identifying problems with the execution. If you need to contact the Business NXT support for help to investigate a problem, you need to provide this unique identifier.
You can find this unique ID in the response of a GraphQL request. Although this was skipped in the examples shown in this tutorial (for simplicity), each response has a field called extensions
that contains an object named vbnxt-trace-id
.
query read($cid : Int, $pagesize : Int) {
useCompany(no: $cid) {
generalLedgerAccount(first: $pagesize) {
totalCount
items {
accountNo
name
}
}
}
}
{
"data": {
"useCompany": {
"generalLedgerAccount": {
"totalCount": 340,
"items": [
{
"accountNo": 1000,
"name": "Forskning og utvikling"
},
{
"accountNo": 1020,
"name": "Konsesjoner"
},
...
]
}
}
},
"extensions": {
"vbnxt-trace-id": "00000000000000000196b4ea383242fa"
}
}
Use the value of the vbnxt-trace-id
when contacting the Business NXT support.
Tip
The same trace identifier can be retrieved from the response headers. GraphQL responses have a custom x-vbc-traceid
header containing the value of the trace identifier.