Controller Troubleshooting Decision Tree
Use this decision tree when a command sent by a controller fails or produces no response. Work through each gate in order — most failures resolve at the first gate where the condition is not met.
Decision gates
1. Auth handshake healthy?
- Expected: relay responds with
auth_ackafterhelloandauthframes. - If no
auth_ack: verifyhello→authordering; confirmaccessTokenis valid and not expired. - On
invalid_access_token: refresh tokens withotto client login, then reconnect.
2. Target node resolved?
- Expected:
targetNodeIdmatches a connected node. - Run
otto commands list(orGET /api/nodes/connected) to see connected nodes. - Ensure
targetNodeIdis set explicitly on every command envelope.
3. ACL granted?
- Expected: the controller client has an ACL grant for the target node.
- On
acl_missing_node_grant: post toPOST /api/controller/accesswith a node bearer token, or use the relay admin ACL flow.
4. Payload valid?
- Expected: action and payload match the command descriptor from
command.list. - Run
otto commands list --site <site>to inspect the input contract. - Fix unknown keys, missing required fields, or type mismatches.
5. Tab context valid?
- Expected:
tabSessionIdreferences a live managed tab with a committed URL matching the command site. - If stale: run
otto cmd --action primitive.tab.opento mint a fresh session. - On
tab_url_not_ready: retry once after a short delay.
6. Stream lifecycle valid?
- Expected:
command.testreturnsstream.listeners; subscribe command returns terminal result;listener_updateevents correlate to subscriberequestId. - Verify unsubscribe or
command_cancelteardown is explicit.
7. Contention issues?
- Expected: tab lock acquired without conflict.
- On
tab_busyortab_locked: retry with bounded backoff, or setwaitPolicy: wait_with_timeout.
Next steps
- requestId Correlation Runbook — trace across components when the tree doesn't resolve the failure.
- Error Codes — full error code reference.
- Reusable Snippets — curl commands for each HTTP gate.