Error Handling
Proper error handling is crucial when working with AI services. RubyLLM provides a comprehensive error handling system that helps you build robust applications.
Error Hierarchy
RubyLLM uses a structured error hierarchy:
RubyLLM::Error # Base error class
RubyLLM::BadRequestError # Invalid request parameters (400)
RubyLLM::UnauthorizedError # API key issues (401)
RubyLLM::PaymentRequiredError # Billing issues (402)
RubyLLM::RateLimitError # Rate limit exceeded (429)
RubyLLM::ServerError # Provider server error (500)
RubyLLM::ServiceUnavailableError # Service unavailable (503)
RubyLLM::ModelNotFoundError # Invalid model ID
RubyLLM::InvalidRoleError # Invalid message role
Basic Error Handling
Wrap your AI interactions in begin/rescue
blocks:
begin
chat = RubyLLM.chat
response = chat.ask "What's the capital of France?"
puts response.content
rescue RubyLLM::Error => e
puts "AI interaction failed: #{e.message}"
end
Handling Specific Errors
Target specific error types for more precise handling:
begin
chat = RubyLLM.chat
response = chat.ask "Generate a detailed analysis"
rescue RubyLLM::UnauthorizedError
puts "Please check your API credentials"
rescue RubyLLM::PaymentRequiredError
puts "Payment required - please check your account balance"
rescue RubyLLM::RateLimitError
puts "Rate limit exceeded - please try again later"
rescue RubyLLM::ServiceUnavailableError
puts "Service temporarily unavailable - please try again later"
rescue RubyLLM::BadRequestError => e
puts "Bad request: #{e.message}"
rescue RubyLLM::Error => e
puts "Other error: #{e.message}"
end
API Response Details
The Error
class contains the original response, allowing for detailed error inspection:
begin
chat = RubyLLM.chat
chat.ask "Some question"
rescue RubyLLM::Error => e
puts "Error: #{e.message}"
puts "Status: #{e.response.status}"
puts "Body: #{e.response.body}"
end
Error Handling with Streaming
When using streaming, errors can occur during the stream:
begin
chat = RubyLLM.chat
chat.ask "Generate a long response" do |chunk|
print chunk.content
end
rescue RubyLLM::Error => e
puts "\nStreaming error: #{e.message}"
end
Handling Tool Errors
When using tools, errors can be handled within the tool or in the calling code:
# Error handling within tools
class Calculator < RubyLLM::Tool
description "Performs calculations"
param :expression,
type: :string,
desc: "Math expression to evaluate"
def execute(expression:)
eval(expression).to_s
rescue StandardError => e
# Return error as structured data
{ error: "Calculation error: #{e.message}" }
end
end
# Error handling when using tools
begin
chat = RubyLLM.chat.with_tool(Calculator)
chat.ask "What's 1/0?"
rescue RubyLLM::Error => e
puts "Error using tools: #{e.message}"
end
Automatic Retries
RubyLLM automatically retries on certain transient errors:
# Configure retry behavior
RubyLLM.configure do |config|
config.max_retries = 5 # Maximum number of retries
end
The following errors trigger automatic retries:
- Network timeouts
- Connection failures
- Rate limit errors (429)
- Server errors (500, 502, 503, 504)
Provider-Specific Errors
Each provider may return slightly different error messages. RubyLLM normalizes these into standard error types, but the original error details are preserved:
begin
chat = RubyLLM.chat
chat.ask "Some question"
rescue RubyLLM::Error => e
if e.response.body.include?("organization_quota_exceeded")
puts "Your organization's quota has been exceeded"
else
puts "Error: #{e.message}"
end
end
Error Handling in Rails
When using RubyLLM in a Rails application, you can handle errors at different levels:
Controller Level
class ChatController < ApplicationController
rescue_from RubyLLM::Error, with: :handle_ai_error
def create
@chat = Chat.create!(chat_params)
@chat.ask(params[:message])
redirect_to @chat
end
private
def handle_ai_error(exception)
flash[:error] = "AI service error: #{exception.message}"
redirect_to chats_path
end
end
Background Job Level
class AiChatJob < ApplicationJob
retry_on RubyLLM::RateLimitError, RubyLLM::ServiceUnavailableError,
wait: :exponentially_longer, attempts: 5
discard_on RubyLLM::UnauthorizedError, RubyLLM::BadRequestError
def perform(chat_id, message)
chat = Chat.find(chat_id)
chat.ask(message)
rescue RubyLLM::Error => e
# Log error and notify user
ErrorNotifier.notify(chat.user, "AI chat error: #{e.message}")
end
end
Monitoring Errors
For production applications, monitor AI service errors:
# Custom error handler
module AiErrorMonitoring
def self.track_error(error, context = {})
# Record error in your monitoring system
Sentry.capture_exception(error, extra: context)
# Log details
Rails.logger.error "[AI Error] #{error.class}: #{error.message}"
Rails.logger.error "Context: #{context.inspect}"
# Return or re-raise as needed
error
end
end
# Usage
begin
chat.ask "Some question"
rescue RubyLLM::Error => e
AiErrorMonitoring.track_error(e, {
model: chat.model.id,
tokens: chat.messages.sum(&:input_tokens)
})
# Show appropriate message to user
flash[:error] = "Sorry, we encountered an issue with our AI service"
end
Graceful Degradation
For critical applications, implement fallback strategies:
def get_ai_response(question, fallback_message = nil)
begin
chat = RubyLLM.chat
response = chat.ask(question)
response.content
rescue RubyLLM::Error => e
Rails.logger.error "AI error: #{e.message}"
# Fallback to alternative model
begin
fallback_chat = RubyLLM.chat(model: 'gpt-3.5-turbo')
fallback_response = fallback_chat.ask(question)
fallback_response.content
rescue RubyLLM::Error => e2
Rails.logger.error "Fallback AI error: #{e2.message}"
fallback_message || "Sorry, our AI service is currently unavailable"
end
end
end
Best Practices
- Always wrap AI calls in error handling - Don’t assume AI services will always be available
- Implement timeouts - Configure appropriate request timeouts
- Use background jobs - Process AI requests asynchronously when possible
- Set up monitoring - Track error rates and response times
- Have fallback content - Prepare fallback responses when AI services fail
- Gracefully degrade - Implement multiple fallback strategies
- Communicate to users - Provide clear error messages when AI services are unavailable
Error Recovery
When dealing with errors, consider recovery strategies:
MAX_RETRIES = 3
def ask_with_recovery(chat, question, retries = 0)
chat.ask(question)
rescue RubyLLM::RateLimitError, RubyLLM::ServiceUnavailableError => e
if retries < MAX_RETRIES
# Exponential backoff
sleep_time = 2 ** retries
puts "Error: #{e.message}. Retrying in #{sleep_time} seconds..."
sleep sleep_time
ask_with_recovery(chat, question, retries + 1)
else
raise e
end
end
Next Steps
Now that you understand error handling in RubyLLM, you might want to explore:
- Rails Integration for using RubyLLM in Rails applications
- Tools for using tools with error handling