Quick diagnostic flow
Symptom index
Schema not bound
Symptoms:GET …/rpc/servicesreturns"services": []with nodescriptor_digest- Invoke returns
404with"device descriptor not found"
device-rpcmodule enabled — inmodules.requiredandsecurity.capabilities. See Omega setup.- Device online — Console shows the device connected over MQTT.
- Device restarted after enabling RPC — the module publishes a schema announce on start and reconnect.
services array before invoking.
Device offline / can’t reach device
Symptoms:- Schema is bound but invoke returns 502, 503, or times out
- Reflect succeeds but invoke never completes
- Error text mentions the device cannot be reached
- Device online in Console — MQTT heartbeat recent.
device-rpcrunning — Omega logs show the gRPC listener started (notmemorytransport only).- MQTT credentials and TLS — cert not expired; broker reachable from the device network.
- Restart the device with MQTT transport and
device-rpcenabled. - Reconnect MQTT (restart Omega or power-cycle) so the platform sees the device again.
- Probe with a unary Echo call:
Root topic mismatch
Symptoms:- Device shows online; schema bound; invoke still fails with reachability or bridge errors
- Works with a local standalone echo server but not when registered in Golain
connection.root_topic must match that assignment.
Check:
Confirm in Console device MQTT details or with
golain omega scaffold.
Fix:
Update Omega client YAML or OMEGA_ROOT_TOPIC, redeploy, and verify list services still returns methods. Then retry invoke.
Permissions
Symptoms:- HTTP 403 with
"message": "forbidden" - HTTP 401 with
"message": "unauthorized"
Authorization: Bearerheader present and token not expiredORG-IDset to a valid organization UUID- Token is a user OIDC token — not a device MQTT credential
can_invoke_rpc on the target device for your user or permission group. Reflect additionally requires can_write_device_metadata.
Update permissions in Console, then retry.
Wrong URL
Symptoms:404 service not found in descriptor404 method not found in descriptor- Invoke hits the management API host instead of the RPC host
- List services and copy exact
services[].nameandmethods[].name:
- Use the full service name (for example
golain.device.v1.EchoService), not a short alias. - POST to the RPC host with no
/core/api/v1prefix:
/streams/{Service}/{Method}. See Invoking methods.
Streaming on wrong path
Symptoms:- HTTP 501 with
"streaming rpc not supported"— you POSTed a server-stream, client-stream, or bidi method to the sync path (/{Service}/{Method}) - HTTP 501 with
"only server-streaming rpc supported"— you POSTed a unary or non-server-stream method to the/streams/path
Example — move
EchoServerStream from sync to streams:
EchoClientStream, EchoBidiStream), use device-local tooling — the cloud does not expose these shapes.
Stale schema after OTA
Symptoms:- New firmware deployed; invoke hits old methods or schema errors
- List services shows an outdated method list
- Device restarted after OTA so it publishes a fresh schema announce.
descriptor_digestfrom list services matches what the device logs after restart.
Queued stuck or duplicates
Symptoms:- Poll shows
pendingorfailedfor a long time - Call becomes
expired— device was offline pastexpires_at - Call shows
succeededbut the device ran the handler twice
- Device online when the platform attempts delivery (queued calls retry on reconnect).
expires_atnot passed — default TTL is 900 s; extend with?ttl_seconds=on enqueue (max 86400).attemptsandlast_erroron the poll response.
ttl_seconds.
Fix — enqueue with longer TTL:
Queued idempotency
Symptoms:- Motor started twice, counter incremented twice, or duplicate writes after one enqueue
succeededin poll but duplicate real-world effects
- Add an idempotency key field to request protos; cache results per key on the device.
- Prefer naturally idempotent handlers where possible.
- Do not rely on
call_id— it is not sent to the device today.
Self-hosted operators
If the device connects and publishes data but RPC schema never binds or invoke never reaches devices on a self-hosted stack, verify apis, mqtt-broker, and worker processes are running and topology is current. See Self-hosted deployment.Operator checklist
Run in order for a single device:API=https://localhost:19090/core/api/v1 and RPC_PROXY=http://localhost:8082.
Still stuck?
- Omega setup — enable
device-rpcon the device - Invoking methods — sync invoke, SSE streaming, and error codes
- API reference — all endpoints
- Queued invoke — async delivery
This guide covers Device RPC only. For legacy shell command RPC (
exec:… over signed control topics), see Remote control — Legacy shell RPC.