Skip to main content

Error handling

The Platform API reports problems in two distinct ways. Knowing which is which tells you whether to retry, fix your request, or surface a domain failure to the operator.

Two kinds of failure

1. Transport, auth, and protocol errors — gRPC status

Anything wrong at the gRPC layer is raised as an error (grpc.RpcError in Python) carrying a status code and a details string. The codes you are most likely to see:

Status codeTypical causeWhat to do
UNAUTHENTICATEDMissing, expired, or invalid tokenRefresh credentials — see Authenticate
PERMISSION_DENIEDToken lacks rights for this call or vesselCheck scope with your engagement
INVALID_ARGUMENTMalformed request (bad field, wrong enum)Fix the request; do not retry
NOT_FOUNDUnknown vessel_id or resourceCheck the id — see Connecting to a vessel
FAILED_PRECONDITIONVessel not in a state that allows the callResolve the precondition, then retry
UNAVAILABLEEndpoint unreachable, link droppedRetry with backoff
DEADLINE_EXCEEDEDCall exceeded its deadlineRetry, or raise the deadline
RESOURCE_EXHAUSTEDFlow-control / rate limitBack off, slow your send rate

2. Domain failures — success / message

Command RPCs (SetArm, SetModeControl, SetMissionPlan, SetControlUnit, SetObjectAlerts, …) return a result with a success flag and a message. A success=false is not a gRPC error: the call was delivered and understood, but the vessel rejected it — invalid mission plan, wrong mode, authority not held, and so on.

Always check success

A command RPC that returns without raising has still only been delivered. Check success before assuming the vessel acted, and confirm the effect on the matching state stream.

resp = client.SetMissionPlan(request)
if not resp.success:
raise RuntimeError(resp.message) # server-side validation error

Catching gRPC errors

Branch on the status code to decide between refresh, retry, and fail-fast:

errors.py
import grpc

try:
response = client.GetVesselState(request)
except grpc.RpcError as err:
code = err.code()
if code == grpc.StatusCode.UNAUTHENTICATED:
... # refresh the token and rebuild the channel
elif code in (grpc.StatusCode.UNAVAILABLE, grpc.StatusCode.DEADLINE_EXCEEDED):
... # transient — retry with backoff
else:
raise # INVALID_ARGUMENT, NOT_FOUND, … — a bug in the request

Retryable vs terminal

Retry only transient codes

Retry UNAVAILABLE, DEADLINE_EXCEEDED, and RESOURCE_EXHAUSTED — they reflect transient link or load conditions. Treat INVALID_ARGUMENT, NOT_FOUND, PERMISSION_DENIED, and (until you refresh credentials) UNAUTHENTICATED as terminal: retrying the same call will fail the same way.

For the backoff pattern itself — and how it applies to streams that end — see Streaming and reconnection.

What's next