Errors
Errors produced by the relay itself are RFC 9457 application/problem+json documents, and carry their code twice: in the Tsrelay-Error header, and as the code field of the body. The type field links to the code's entry on this page, such as https://tsrelay.mazetti.me/errors/tunnel_unavailable; detail says what exactly went wrong.
{
"type": "https://tsrelay.mazetti.me/errors/tunnel_unavailable",
"title": "Tunnel Unavailable",
"status": 561,
"detail": "no tunnel is attached to this hook",
"code": "tunnel_unavailable"
}
Responses from your app are never marked, including its own 5xx. Tunnel and transport failures have statuses of their own, 561 to 565, so they can't be mistaken for your app's 502, 503 or 504; clients that don't know them treat them as 500.
Calling a hook
Senders see these when they call a hook.
sender_forbidden
403. The caller has no sender grant: calling a hook needs the sender role on the relay. Ask a relay admin to grant the calling device the sender role; see Access control.
hook_not_found
404. No hook answers at this path, the hook is in a channel the caller can't use, or it was deleted or expired while a call was held for it. Check the URL; its owner can list their hooks with tsrelay hooks list.
body_stalled
408. The sender paused the request body for longer than the hook's idleTimeout. The hook's owner can raise it with tsrelay hooks update <hook> --idle-timeout 1m.
body_too_large
413. The request body is larger than the hook's maxBodyBytes, or, for a call from the internet, larger than the relay's --funnel-max-body-bytes. The hook's owner can raise the limit with tsrelay hooks update <hook> --max-body-bytes 10485760, up to the relay's maximum.
path_too_long
414. The call's path, percent-encoded, is longer than 8 KiB. The relay refuses it before routing, like a server refusing an overlong request line, so it doesn't reach the inspector or the audit log. Shorten the path; send long data in the body.
shutting_down
503. The relay is shutting down, usually for a restart, while the call waited for a tunnel; one arriving then gets it too. Calls already delivered to a tunnel finish first. Retry-After is 5: retry then, and the restarted relay holds the call until the tunnel is back.
rate_limited
429. The hook's rateLimit was exceeded. Retry-After is the limiter's wait, rounded up to whole seconds. Wait, then retry; the hook's owner can raise the limit with tsrelay hooks update <hook> --rate-limit 100/200.
tunnel_unavailable
561 Tunnel Unavailable. No tunnel attached to the hook within its holdTimeout. Retry-After is the holdTimeout, but never less than 5 s. The hook's owner can attach one with tsrelay attach <hook> --to 3000, or wait longer with tsrelay hooks update <hook> --hold-timeout 1m.
hold_queue_full
562 Tunnel Overloaded. No tunnel is attached to the hook, and too many calls are already waiting for one (the relay's --hook-hold-queue). Retry-After is the holdTimeout, but never less than 5 s. The hook's owner can attach a tunnel with tsrelay attach <hook> --to 3000.
tunnel_overloaded
562 Tunnel Overloaded. Too many calls are in flight on the tunnel at once (the relay's --tunnel-concurrency). Retry-After is 1; retry in a moment.
tunnel_lost
563 Tunnel Lost. The tsrelay process serving the hook disconnected while delivering the call. It reconnects on its own; check that it still runs.
upstream_unreachable
564 Upstream Unreachable. The tunnel is up, but nothing answered at its target. Start the app, or check the port or URL tsrelay forwards to.
upstream_timeout
565 Upstream Timeout. The app didn't send its response headers within the hook's responseTimeout, or its response stalled past the idleTimeout. The hook's owner can raise it with tsrelay hooks update <hook> --response-timeout 1m.
Call outcomes
A call's outcome in the inspector and the API is pending, ok, one of the codes above, or sender_canceled: the sender went away before the call finished, so no response was sent and no error is recorded against the hook.
Using the API
The API, the web UI and the CLI see these. Where one says the relay may not speak your tsrelay's API, install the relay's version of the CLI; see Updating the CLI.
forbidden
403. Your grants don't allow this: a missing role, or a hook you can see but only its owner can act on. Ask a relay admin to grant you the role; the relay's web UI shows them the grant.
not_identified
403. Tailscale couldn't tell the relay who is calling, so no grant applies. Check tailscale status, and reach the relay over Tailscale, not another network.
cross_site
403. An API write came from another site, as the browser's Sec-Fetch-Site header says. API writes must come from the relay's own web UI or from a tool such as the CLI.
channel_forbidden
403. The hook is in a channel your grants no longer match, so a tunnel can't serve it. Ask a relay admin to grant you the channel.
stream_limit
429. The node already has 8 event streams open, the most the relay allows one node. Close a browser tab or a client that watches the relay, then retry.
tunnel_limit
403. You already have as many tunnels attached as your maxTunnels grant allows. Stop one you no longer need with tsrelay stop, or ask a relay admin to raise the limit.
not_found
404. The relay has no such hook or call for you. tsrelay hooks list shows the hooks you own.
invalid
422. Something sent isn't valid, such as a hook setting above the relay's maximum. The problem's errors field names each one; correct them, then retry.
unsupported_field
422. The request has a field this relay doesn't know, so it is newer than the relay; within one major version, this is how an older relay says so. The problem's errors field names each one, like invalid does. Ask the relay's operator to upgrade the relay, or install the CLI version the relay runs.
bad_request
400. The relay couldn't read the request: malformed JSON, a tunnel request without its upgrade headers, or one that names no hook. This tsrelay may not speak the relay's API.
not_public
409. The hook isn't public, so it has no public URL to rotate. Make it public first with tsrelay hooks update <hook> --public.
superseded
409. A newer tunnel has taken the hook over, and a tunnel that reconnects after being replaced may not take it back. Stop the other tunnel, or attach again without resuming.
attach_rejected
409. The hook's attachPolicy is reject and a tunnel is already attached. Stop that one, or set the policy to takeover with tsrelay hooks update <hook> --attach-policy takeover.
call_pending
409. The call is still being delivered, so it can't be replayed yet. Retry once it has an outcome.
call_refused
409. The relay refused the call by channel and never delivered it, so there is nothing to replay. Send a new test request instead.
not_captured
409. The call can't be replayed because its request wasn't captured in full: bodies aren't captured for the hook, or the body or the query string was cut off at the capture limit. Turn capture on with tsrelay hooks update <hook> --capture to replay future calls.
misdirected_request
421. The relay doesn't answer to the host name the request used. Check --relay and TSRELAY_RELAY.
unsupported_media_type
415. API requests with a body must be application/json. This tsrelay may not speak the relay's API.
not_acceptable
406. The resource isn't available in the representation the Accept header or extension asked for; the detail lists those it is available in.
method_not_allowed
405. The resource exists, but doesn't take this method. This tsrelay may not speak the relay's API.
internal
500. Something went wrong on the relay itself, such as failing to save a hook. Retry; if it keeps failing, ask the relay's operator to check its logs.