Skip to content

Troubleshooting

What a failed request looks like, how to read it, and fixes for the failures that come up most often.

Every failed request returns a JSON body. There are two shapes, and you tell them apart by one thing: whether details is present.

  • details present — the error came from the service, and code is the numeric gRPC status code.
  • details absent — the error came from the gateway, before the request reached the service, and code is a copy of the HTTP status.

The same rule holds for 4xx and 5xx.

A service error:

{
"code": 3,
"message": "validation failed",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{ "field": "playerid", "description": "required" }
]
},
{
"@type": "type.googleapis.com/google.rpc.RequestInfo",
"requestId": "1ff721182e0466bb3657575ef53c19ad"
}
]
}

A gateway error:

{ "code": 401, "message": "Jwt is missing" }
HTTP code gRPC code When
400 3 INVALID_ARGUMENT Validation failed. Carries BadRequest — read fieldViolations.
400 9 FAILED_PRECONDITION The request is well-formed but the state it needs is not there — e.g. a device referencing a location that does not exist.
401 401 — Missing, malformed or expired token. From the gateway, so no details.
403 7 PERMISSION_DENIED Authenticated, but not allowed this resource or namespace.
404 5 NOT_FOUND The addressed resource does not exist.
409 6 ALREADY_EXISTS The resource is already there under a different identity.
500 13 INTERNAL Server-side failure. Safe to retry with backoff.

google.rpc.BadRequest carries one entry per rejected field.

The proto field path, which is snake_case — not the camelCase you sent in the request body:

Example Means
playerid a top-level field
location.external_id dotted for nested messages
0.availability index first for an entry in a repeated field

Always begins with one of these words, optionally followed by ": " and a specific explanation:

Prefix Means
required Missing. An *_UNSPECIFIED enum value counts as missing.
invalid Present but not acceptable — e.g. invalid: products requires a device with SENSOR_BASKET.
conflict Clashes with something already stored.
not found Names something that does not exist.
not ready Exists, but not in a state that allows this.
unknow Unclassified failure. (Spelled without the trailing n.)

Branch on field plus the leading word, not on the full string — the text after ": " is explanatory and may change.

google.rpc.RequestInfo.requestId is the trace id for the request, and it rides every service error. Log it, and quote it to us when raising something — it is how we find your request.

There is no ErrorInfo, so there is no stable machine-readable reason or domain to switch on. BadRequest.fieldViolations[] is the machine-readable part.

Error invalid_client or invalid_grant on the token request

Section titled “Error invalid_client or invalid_grant on the token request”

Your client_id or client_secret is wrong. Double-check both, and make sure the @ in the client ID is URL-encoded as %40 in the form body.

The X-Verve-Namespace: project:<PROJECT_ID> header is missing. Add it to every Inventory API request.

Your access token is missing, expired, or malformed. Confirm the Authorization: Bearer <access_token> header is set, and request a fresh token if the current one has expired — see Authentication.

Authentication succeeded but the request isn’t authorized. Either your service account lacks permission on the project, or the X-Verve-Namespace header is missing or incorrect. Verify it is set to project:<PROJECT_ID>.