Skip to content

Error Catalog

Operations never throw — each returns a typed result, either success with a payload or a typed error. That uniform contract is what lets the engine apply consistent retry, rollback, and HTTP/GraphQL mapping across every operator.

Each error falls into a category that drives engine behaviour:

CategoryMeaningRetried?
TransientTemporary — timeout, rate-limit, connection, upstream-unavailable, lock conflict.Yes
PermanentWon’t succeed on retry — bad input, auth failure, not-found, parse error, bad config.No
InfrastructurePlatform-level — file I/O, storage, backup failure.No
BusinessFlow-level — step failure, flow error, human-task, staging, share.No

The engine retries Transient errors per the step’s retry: policy; once attempts run out, a retry-exhausted error wraps the last failure.

A representative slice of the error codes and how they surface:

CodeHTTPGraphQL
timeout504TIMEOUT
authentication-error401UNAUTHORIZED
authorization-error403FORBIDDEN
invalid-parameters400BAD_USER_INPUT
invalid-configuration400BAD_USER_INPUT
not-found404NOT_FOUND
parse-error400BAD_USER_INPUT
storage-error500INTERNAL_ERROR
step-invocation-error500INTERNAL_ERROR
staging-error500STAGING_ERROR
compensation-error500INTERNAL_ERROR
retry-exhausted500INTERNAL_ERROR

compensation-error is the rollback-failed code — it surfaces when a saga compensation itself fails, which is the situation you most want surfaced loudly.

In practice ~95% of operator failures are a single OperatorError whose message determines its class. These classes are what an operator declares in its definition.yaml:

ClassCategoryRetryableTypical message
validationPermanentNo"{Field} is required"
not-foundPermanentNo"{Entity} '{id}' not found"
authPermanentNo"Authentication failed", "Invalid API key"
timeoutTransientYesoperation exceeded its time limit
rate-limitTransientYesHTTP 429, "Rate limit exceeded"
connectionTransientYes"Connection refused", "Connection reset"
unavailableTransientYesHTTP 502/503/504, "overloaded"
api-errorPermanentNo"{API} error ({code}): {message}"
parsePermanentNo"Invalid JSON", "Failed to parse"
configPermanentNo"{Field} is required when {Condition}"
conflictTransientYes"Database locked", "File is locked"
quotaPermanentNo"File too large", "Max results exceeded"
storageInfrastructureNo"Path does not exist", S3/disk errors

A failed invocation reports the error code and category in its log and in the REST/GraphQL response body. Transient failures retry automatically; permanent ones fail fast so you fix the flow rather than waiting through retries. See Common Pitfalls for the most frequent permanent (validation / invalid-parameters) cases and their fixes.