Appearance
Timeouts
Every request/reply call has a timeout. If no reply arrives within the window, the caller's promise rejects locally and the reply subscription is torn down.
The default
typescript
// projects/matrix-3/packages/core/src/engine/remoting/RequestReply.ts:263
const timeoutMs = opts.timeoutMs ?? 5000;The default is 5000 ms (5 seconds). Callers that need a longer or shorter timeout pass timeoutMs in the per-call options:
typescript
const result = await actor.invoke(targetMount, op, payload, { timeoutMs: 30_000 });When to deviate from the default
| Situation | Recommended timeout |
|---|---|
| Local in-process call (in-memory transport) | 1000 ms |
| Local NATS, fast actor | 5000 ms (default) |
| Cross-root call through HiveCast leaf | 10_000 ms |
| Inference call (LLM startup) | 30_000 ms |
| Long-running reconciliation, builder, install | 120_000 ms |
| Factory ops that load a package | 15_000 ms (per session memory) |
These are practical numbers from production code paths. The 3-second default-tool-call timeout and 15-second factory timeout are documented in the user memory; the protocol-level default is 5 seconds.
What happens on timeout
1. caller publishes request, subscribes on $reply.<cid>
2. timer starts (timeoutMs)
3. timer fires before reply
4. caller unsubscribes from $reply.<cid>
5. caller's promise rejects with a timeout errorThe actor's response, if it eventually arrives, lands on a subject with no subscribers. NATS drops it.
Timeout vs error
Timeouts are transport-level rejections. There is no envelope on the wire describing them — the timeout is constructed in the caller's process. Errors are application-level replies in the form { ok: false, error } delivered as a normal response envelope.
| Symptom | Cause |
|---|---|
Error: request timed out after 5000ms | No reply arrived in time |
{ ok: false, error: "no inference configured" } | Actor handled the request and returned an error |
Error: NATS: connection closed | Transport disconnected mid-request |
Error: NATS: subject permissions violation | NATS ACL rejected the publish |
Callers must treat these differently. Timeout = retryable (the actor may have processed the request anyway, so include idempotencyKey). Application error = caller's logic flaw or actor-side condition. Connection errors = transport issue, retry with backoff.
Idempotency on retry
Per CLAUDE.md Rule 9: "Every write op accepts optional idempotencyKey."
Callers that retry on timeout should pass the same idempotencyKey so the actor can deduplicate. Without idempotency, a retry after timeout may double-execute the op (the original request might still be in-flight when the retry lands).
What times out from the actor's view
From the actor's perspective, the timeout is invisible. The actor handles the inbox message and publishes a reply whether or not the caller is still listening. The actor MAY notice that its reply subject has no subscribers (NATS does not deliver to zero subscribers and the publish silently succeeds), but there is no callback for "the caller gave up".
Long-running ops should ack quickly and switch to a session pattern, not hold the request open for a minute. See Sessions.
Background timers
Some actor handlers perform background work after replying. The reply indicates the request was received and queued, not that all side effects have completed. Callers that need to confirm completion should subscribe to the actor's $events for the relevant completion event.
Timeout classification (target)
Status: target state. A formal classification of timeout causes (transport-level vs actor-level vs upstream-service-level) is on the backlog. Today the caller sees only "request timed out". A future revision would surface causes through error codes (
DEADLINE_EXCEEDEDfor transport;UPSTREAM_TIMEOUTfor actor-reported deadlines on calls into services it depends on).
See also
- Request envelope — where
correlationId/replyToare set. - Response envelope — the success path.
- Error envelope — application-level errors.
- Correlation IDs — reply subscription teardown on timeout.
- Compatibility rules — schema evolution affecting timeouts.