2.4 KiB
2.4 KiB
Error Handling
CallError
All operational errors are represented as CallError, which extends Error with a structured code and optional details:
class CallError extends Error {
readonly code: CallErrorCode;
readonly details?: unknown;
}
import { CallError, InfrastructureErrorCode } from "@alkdev/operations";
throw new CallError(InfrastructureErrorCode.OPERATION_NOT_FOUND, "Operation not found: foo.bar", { operationId: "foo.bar" });
Infrastructure Error Codes
Built-in codes for framework-level errors:
| Code | When |
|---|---|
OPERATION_NOT_FOUND |
No spec or handler registered for the operation ID |
ACCESS_DENIED |
Caller lacks required scopes or resource access |
VALIDATION_ERROR |
Input fails schema validation |
TIMEOUT |
Call or subscription timed out (deadline or idle) |
ABORTED |
Request was explicitly aborted |
EXECUTION_ERROR |
Handler threw an Error that didn't match any declared error code |
UNKNOWN_ERROR |
Non-Error value thrown from handler |
You can also use custom error codes as strings:
throw new CallError("INVALID_INPUT", "Title is required", { field: "title" });
Declared Errors
Operations can declare expected error codes in their spec:
{
errorSchemas: [
{ code: "INVALID_INPUT", description: "Input validation failed", schema: Type.Object({ field: Type.String() }) },
{ code: "NOT_FOUND", description: "Task not found", schema: Type.Object({ id: Type.String() }) },
],
}
mapError
mapError() normalizes thrown values into CallError:
import { mapError } from "@alkdev/operations";
const callError = mapError(thrownValue, spec.errorSchemas);
Logic:
- If already a
CallError, returns it as-is - If an
Error, checks if its message matches any declared error code prefix (CODE:or exactCODE) — returns aCallErrorwith that code - Otherwise, returns
CallError(EXECUTION_ERROR, error.message, error)
This is used internally by buildCallHandler() to map handler errors into the call protocol error format.
Error Propagation
| Context | Behavior |
|---|---|
registry.execute() |
Throws CallError directly |
subscribe() |
Throws CallError into the async generator |
PendingRequestMap |
Emits call.error event, rejected promise or stopped stream |
buildEnv() calls |
Propagates CallError from the nested execute() |