Interruptions - Faults¶
Faults are exceptions raised by execute! when tasks halt. They carry rich context about execution state, enabling sophisticated error handling patterns.
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:
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