Interruptions - Faults¶
Faults are exceptions raised by execute! when tasks halt. They carry rich context about execution state, enabling sophisticated error handling patterns. See the Exceptions Reference for the full hierarchy.
Fault Types¶
| Type | Triggered By | Use Case |
|---|---|---|
CMDx::Fault |
Base class | Catch-all for any interruption |
CMDx::SkipFault |
skip! method |
Optional processing, early returns |
CMDx::FailFault |
fail! method |
Validation errors, processing failures |
Important
All faults inherit from CMDx::Fault and expose result, task, context, and chain data.
Fault Handling¶
begin
ProcessTicket.execute!(ticket_id: 456)
rescue CMDx::SkipFault => e
logger.info "Ticket processing skipped: #{e.message}"
schedule_retry(e.context.ticket_id)
rescue CMDx::FailFault => e
logger.error "Ticket processing failed: #{e.message}"
notify_admin(e.context.assigned_agent, e.result.metadata[:error_code])
rescue CMDx::Fault => e
logger.warn "Ticket processing interrupted: #{e.message}"
rollback_changes
end
Data Access¶
Access rich execution data from fault exceptions:
begin
LicenseActivation.execute!(license_key: key, machine_id: machine)
rescue CMDx::Fault => e
# Result information
e.result.state #=> "interrupted"
e.result.status #=> "failed" or "skipped"
e.result.reason #=> "License key already activated"
# Task information
e.task.class #=> <LicenseActivation>
e.task.id #=> "abc123..."
# Context data
e.context.license_key #=> "ABC-123-DEF"
e.context.machine_id #=> "[FILTERED]"
# Chain information
e.chain.id #=> "def456..."
e.chain.size #=> 3
end
Advanced Matching¶
Task-Specific Matching¶
Handle faults only from specific tasks using for?:
begin
DocumentWorkflow.execute!(document_data: data)
rescue CMDx::FailFault.for?(FormatValidator, ContentProcessor) => e
# Handle only document-related failures
retry_with_alternate_parser(e.context)
rescue CMDx::SkipFault.for?(VirusScanner, ContentFilter) => e
# Handle security-related skips
quarantine_for_review(e.context.document_id)
end
Custom Logic Matching¶
begin
ReportGenerator.execute!(report: report_data)
rescue CMDx::Fault.matches? { |f| f.context.data_size > 10_000 } => e
escalate_large_dataset_failure(e)
rescue CMDx::FailFault.matches? { |f| f.result.metadata[:attempt_count] > 3 } => e
abandon_report_generation(e)
rescue CMDx::Fault.matches? { |f| f.result.metadata[:error_type] == "memory" } => e
increase_memory_and_retry(e)
end
Fault Propagation¶
Propagate failures with throw! to preserve context and maintain the error chain.
Important
throw! requires a CMDx::Result argument. Passing any other object raises a TypeError.
Basic Propagation¶
class ReportGenerator < CMDx::Task
def work
# Throw if skipped or failed
validation_result = DataValidator.execute(context)
throw!(validation_result)
# Only throw if skipped
check_permissions = CheckPermissions.execute(context)
throw!(check_permissions) if check_permissions.skipped?
# Only throw if failed
data_result = DataProcessor.execute(context)
throw!(data_result) if data_result.failed?
# Continue processing
generate_report
end
end
Additional Metadata¶
class BatchProcessor < CMDx::Task
def work
step_result = FileValidation.execute(context)
if step_result.failed?
throw!(step_result, {
batch_stage: "validation",
can_retry: true,
next_step: "file_repair"
})
end
continue_batch
end
end
Chain Analysis¶
Trace fault origins and propagation through the execution chain:
result = DocumentWorkflow.execute(invalid_data)
if result.failed?
# Trace the original failure
original = result.caused_failure
if original
puts "Original failure: #{original.task.class.name}"
puts "Reason: #{original.reason}"
end
# Find what propagated the failure
thrower = result.threw_failure
puts "Propagated by: #{thrower.task.class.name}" if thrower
# Analyze failure type
case
when result.caused_failure?
puts "This task was the original source"
when result.threw_failure?
puts "This task propagated a failure"
when result.thrown_failure?
puts "This task failed due to propagation"
end
end