Skip to content

Errors Reference

Horsies has three relevant error surfaces:

  • runtime task/workflow results returned in TaskResult<T>
  • send-time errors returned in TaskSendResult<T>
  • startup or validation errors returned as HorsiesError

Runtime errors are returned via TaskResult<T>. The TaskError.error_code field contains either a TaskErrorCode::BuiltIn(BuiltInTaskCode) or a TaskErrorCode::User(String).

When a task reaches a terminal Failed state, the error_code is persisted to horsies_tasks.error_code for queryability. Access it via TaskInfo.error_code from handle.info().

Each execution attempt is also recorded in horsies_task_attempts with per-attempt error_code, error_message, and outcome.

FieldTypeDescription
error_codeOption<TaskErrorCode>Library or domain error code
messageOption<String>Human-readable description
dataOption<serde_json::Value>Additional context
causeOption<serde_json::Value>Original cause (serialized as "exception" on wire)
pub enum TaskErrorCode {
BuiltIn(BuiltInTaskCode),
User(String),
}

Built-in codes are serialized as {"__builtin_task_code__": "CODE_NAME"}. User strings are serialized as plain strings.

All library-defined error codes are grouped into four families.

Errors from task execution, broker, and worker internals.

CodeDescriptionAuto-Retry?
UnhandledErrorUncaught panic or error in task codeYes, if in auto_retry_for
TaskErrorTask function returned an errorYes, if in auto_retry_for
WorkerCrashedWorker died during executionYes, if in auto_retry_for
BrokerErrorDatabase or broker failureNo
WorkerResolutionErrorFailed to resolve task by nameNo
WorkerSerializationErrorFailed to serialize/deserialize task dataNo
ResultDeserializationErrorStored result JSON is corruptNo
WorkflowEnqueueFailedWorkflow node failed during enqueueNo
SubworkflowLoadFailedSubworkflow definition could not be loadedNo

Errors from type/schema validation and structural contracts.

CodeDescriptionAuto-Retry?
ArgumentTypeMismatchArguments don’t match expected typeNo
ReturnTypeMismatchReturn type doesn’t match declarationNo
WorkflowCtxMissingIdWorkflow context is missing required IDNo

Errors from handle.get(). These indicate issues retrieving the result, not task execution failures.

CodeDescriptionAuto-Retry?
WaitTimeoutget() timed out; task may still be runningNo
TaskNotFoundTask ID doesn’t exist in databaseNo
WorkflowNotFoundWorkflow ID doesn’t exist in databaseNo
ResultNotAvailableResult cache was never setNo
ResultNotReadyResult not yet available; task is still runningNo

Terminal outcome codes for tasks and workflows.

CodeDescriptionAuto-Retry?
TaskCancelledTask was cancelled before completionNo
TaskExpiredgood_until deadline passed before execution startedNo
WorkflowPausedWorkflow was pausedNo
WorkflowFailedWorkflow failedNo
WorkflowCancelledWorkflow was cancelledNo
UpstreamSkippedUpstream task in workflow was skippedNo
SubworkflowFailedSubworkflow failedNo
WorkflowSuccessCaseNotMetWorkflow success condition was not satisfiedNo
WorkflowStoppedWorkflow was stoppedNo
SendSuppressedSend was suppressed (e.g. during check phase)No

Send errors are returned via TaskSendResult<TaskHandle<T>>. These are separate from TaskResult — they indicate that enqueuing the task itself failed, not that task execution failed.

FieldTypeDescription
codeTaskSendErrorCodeFailure category
messageStringHuman-readable description
retryableboolWhether the caller can retry with the same payload
task_idOption<String>Generated task ID
payloadOption<TaskSendPayload>Serialized envelope for idempotent retry
CodeDescriptionRetryable
SendSuppressedSend suppressed during check phaseNo
ValidationFailedArgument serialization or validation failedNo
EnqueueFailedBroker/database failure during enqueue (transient)Yes
PayloadMismatchRetry payload SHA does not matchNo

Domain-specific errors use string codes via TaskErrorCode::User:

#[task("validate_order")]
async fn validate_order(input: OrderInput) -> Result<OrderResult, TaskError> {
if input.total > 10000 {
return Err(TaskError::new(
"ORDER_LIMIT_EXCEEDED",
format!("Order {} exceeds limit", input.order_id),
));
}
Ok(OrderResult { status: "valid".into() })
}

User-defined codes are auto-retried when listed in auto_retry_for:

#[task(
"call_api",
auto_retry_for = ["RATE_LIMITED", "SERVICE_UNAVAILABLE"],
retry_policy = RetryPolicy::fixed(vec![30, 60, 120], true)?
)]
async fn call_api(input: ApiInput) -> Result<ApiResponse, TaskError> {
// ...
}

Startup errors are returned during app initialization, workflow validation, or task registration. If a startup error occurs, the worker or scheduler will fail to start.

These errors use ErrorCode (not BuiltInTaskCode) and indicate structural or configuration issues.

pub struct HorsiesError {
message: String,
code: Option<ErrorCode>,
notes: Vec<String>,
}
RangeCategoryDescription
HRS-001–HRS-099Workflow ValidationInvalid workflow specification
HRS-100–HRS-199Task DefinitionInvalid task macro usage
HRS-200–HRS-299ConfigurationInvalid app, broker, CLI, or worker configuration
HRS-300–HRS-399RegistryTask/workflow registry failures
CodeNameDescription
HRS-001WorkflowNoNameWorkflow has no name
HRS-002WorkflowNoNodesWorkflow has no nodes
HRS-003WorkflowInvalidNodeIdInvalid node ID reference
HRS-004WorkflowDuplicateNodeIdDuplicate node ID
HRS-005WorkflowNoRootTasksNo root tasks in workflow
HRS-006WorkflowInvalidDependencyInvalid dependency reference
HRS-007WorkflowCycleDetectedCycle detected in workflow DAG
HRS-008WorkflowInvalidArgsFromInvalid args_from binding
HRS-009WorkflowInvalidCtxFromInvalid workflow_ctx_from binding
HRS-010WorkflowCtxParamMissingTask expects workflow context but binding is missing
HRS-011WorkflowInvalidOutputInvalid workflow output node
HRS-012WorkflowInvalidSuccessPolicyInvalid success policy configuration
HRS-013WorkflowInvalidJoinInvalid join configuration
HRS-014WorkflowUnresolvedQueueQueue could not be resolved for a task node
HRS-015WorkflowUnresolvedPriorityPriority could not be resolved for a task node
HRS-016WorkflowNoDefinitionKeyMissing workflow definition key where required
HRS-017WorkflowDuplicateDefinitionKeyDuplicate workflow definition key
HRS-018WorkflowSubworkflowAppMissingSubworkflow app/registry context missing
HRS-019WorkflowInvalidKwargKeyInvalid kwarg key in injected workflow data
HRS-020WorkflowMissingRequiredParamsWorkflow input is missing required params
HRS-025WorkflowOutputTypeMismatchWorkflow output type does not match declared type
HRS-027WorkflowCheckCasesRequiredWorkflow check builder requires cases
HRS-028WorkflowCheckCaseInvalidInvalid workflow check case
HRS-029WorkflowCheckBuilderExceptionWorkflow check builder raised an exception
HRS-030WorkflowCheckUndecoratedBuilderWorkflow check builder was not decorated correctly
HRS-032WorkflowArgsWithInjectionWorkflow mixes whole args with injected params in an invalid way
CodeNameDescription
HRS-100TaskNoReturnTypeTask is missing a return type
HRS-101TaskInvalidReturnTypeTask return type is not supported
HRS-102TaskInvalidOptionsInvalid task options
HRS-103TaskInvalidQueueInvalid task queue configuration
CodeNameDescription
HRS-200ConfigInvalidQueueModeInvalid queue mode configuration
HRS-201ConfigInvalidClusterCapInvalid cluster capacity configuration
HRS-202ConfigInvalidPrefetchInvalid prefetch configuration
HRS-203BrokerInvalidUrlInvalid broker URL
HRS-204ConfigInvalidRecoveryInvalid recovery configuration
HRS-205ConfigInvalidScheduleInvalid schedule configuration
HRS-206CliInvalidArgsInvalid CLI arguments
HRS-207WorkerInvalidLocatorInvalid worker locator configuration
HRS-211BrokerInitFailedBroker initialization failed
HRS-212CheckReservedCodeCollisionReserved built-in code collides with user retry configuration
CodeNameDescription
HRS-300TaskNotRegisteredTask not found in registry
HRS-301TaskDuplicateNameDuplicate task name
HRS-302WorkflowUnregisteredTaskWorkflow references a task that was not registered

Use app.check() for static validation or app.check_live() to additionally connect to PostgreSQL, ensure the schema, and verify connectivity:

// Static validation only
app.check()?;
// Static + schema ensure + database ping
app.check_live().await?;