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 code | Typical cause | What to do |
|---|---|---|
UNAUTHENTICATED | Missing, expired, or invalid token | Refresh credentials — see Authenticate |
PERMISSION_DENIED | Token lacks rights for this call or vessel | Check scope with your engagement |
INVALID_ARGUMENT | Malformed request (bad field, wrong enum) | Fix the request; do not retry |
NOT_FOUND | Unknown vessel_id or resource | Check the id — see Connecting to a vessel |
FAILED_PRECONDITION | Vessel not in a state that allows the call | Resolve the precondition, then retry |
UNAVAILABLE | Endpoint unreachable, link dropped | Retry with backoff |
DEADLINE_EXCEEDED | Call exceeded its deadline | Retry, or raise the deadline |
RESOURCE_EXHAUSTED | Flow-control / rate limit | Back 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.
successA 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:
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 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
- Streaming and reconnection — recover dropped streams.
- Troubleshooting — symptoms, causes, and fixes.