Appearance
Error envelope
The Matrix protocol does not use a separate error envelope shape. Errors are returned as a regular response envelope (op: '$reply') whose payload includes ok: false plus an error string and optional structured fields. This keeps the wire shape uniform — any caller code that parses replies parses errors with the same path.
The shape
typescript
interface MxErrorPayload {
ok: false;
error: string; // human-readable message
code?: string; // machine-readable code, e.g. 'NOT_FOUND'
statusCode?: number; // for gateway / HTTP-mapped errors
details?: unknown; // op-specific structured detail
// … any op-specific fields
}A complete error reply:
json
{
"op": "$reply",
"correlationId": "f12d-7af0",
"payload": {
"ok": false,
"error": "gateway.http.resolve requires a non-empty pathname",
"statusCode": 400
}
}Real examples from the running code
From projects/matrix-3/packages/system-gateway-http/src/index.ts lines 294–340:
typescript
async onGatewayHttpResolve(payload): Promise<...> {
const normalized = normalizeRequestPath(payload.pathname);
if (!normalized) {
return { ok: false, error: 'gateway.http.resolve requires a non-empty pathname' };
}
// ...
return {
ok: resolution !== null,
pathname: normalized,
resolution,
...(resolution ? {} : { error: 'No Host runtime webapp route matched the request' }),
};
}
async onGatewayHttpRequest(payload): Promise<...> {
const method = (readString(payload.method) || 'GET').toUpperCase();
if (method !== 'GET' && method !== 'HEAD') {
return {
ok: false,
statusCode: 405,
headers: { allow: 'GET, HEAD' },
error: 'gateway.http.request only supports GET and HEAD',
};
}
// ...
return {
ok: false,
statusCode: 404,
pathname: normalized,
error: 'No Host runtime webapp route matched the request',
};
}From projects/matrix-3/packages/system-runtimes/src/RuntimeManagerActor.ts line 241:
typescript
return {
ok: false,
error: 'runtimes.reconcile is not available in daemonless Host mode; use Host Service package/runtime APIs.',
};These show three patterns that appear consistently:
- Always set
ok: falsewhen the op cannot fulfill the request. - Always set a human-readable
error, written for an operator who sees it in a log. - Add structured fields when the caller needs them:
statusCodefor gateway errors, mount/path identifiers when relevant.
Conventions
| Field | When to include |
|---|---|
ok | Always. false is mandatory on errors. |
error | Always. Plain English message. |
code | When the caller might pattern-match (e.g., NOT_FOUND, TIMEOUT, UNAUTHENTICATED). See Error codes. |
statusCode | When the op maps to an HTTP-style status. Used by system.gateway.http and gateway adapters. |
details | Op-specific structured data the caller can use programmatically. |
When the actor throws
If an op handler throws an unhandled exception, the framework catches it and replies with { ok: false, error: <message> }. Callers should not rely on this shape for error classification — actors should explicitly construct error responses, because the throw path does not include structured details.
When the transport fails
A request that times out (no reply within the timeout window) produces a transport-level rejection at the caller — there is no envelope on the wire to populate. The caller's promise rejects with a timeout error constructed locally. See Timeouts.
A request that fails at the NATS layer (publish error, connection closed) likewise rejects locally without a server-side envelope.
Errors vs $reply with empty payload
Returning { ok: true } with no other fields is a valid success response — it means "op completed; nothing to return". Returning { ok: false, error } is an error. Returning {} is ambiguous and should be avoided; new ops should always set ok explicitly.
Error code reference
Standardized error codes live in Error codes. The current set of codes seen in production is small and op-specific; the workstream goal is to converge on a shared catalog (NOT_FOUND, INVALID_ARGUMENT, UNAUTHENTICATED, PERMISSION_DENIED, UNAVAILABLE, INTERNAL, DEADLINE_EXCEEDED, ALREADY_EXISTS, RESOURCE_EXHAUSTED).
Status: target state, partial. Error code standardization is in progress. Many actors today return only
error: <message>without a code. Document the codes your actor uses throughstatic errorSpecs(declared onMatrixActor, surfaced via$introspect).
See also
- Response envelope — the matching success shape.
- Timeouts — caller-side rejection semantics.
- Error codes — code catalog (target).
- Envelope schemas — full schema.