CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-agentic

Quarkus extension that integrates LangChain4j's agentic capabilities, enabling developers to build AI agent-based applications using declarative patterns with support for multiple agent types, agent-to-agent communication, and CDI integration.

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

This document describes how to handle errors gracefully with error context information and recovery strategies.

Capabilities

Error Context

Provides context information when an error occurs during agent execution.

/**
 * Context information for errors during agent execution
 */
public interface ErrorContext {
    /**
     * Gets the name of the agent that encountered the error
     * @return Agent name
     */
    String agentName();

    /**
     * Gets the exception that occurred
     * @return The exception
     */
    Throwable exception();

    /**
     * Gets access to the agentic scope
     * @return AgenticScope instance for reading/writing state
     */
    AgenticScope agenticScope();
}

Usage Example:

import dev.langchain4j.agentic.agent.ErrorContext;
import dev.langchain4j.agentic.agent.ErrorRecoveryResult;

public interface ErrorAwareAgent {
    @Agent(description = "Processes with error handling", outputKey = "result")
    String process(@V("data") String data);

    @ErrorHandler
    static ErrorRecoveryResult handleError(ErrorContext context) {
        // Access error information
        String agentName = context.agentName();
        Throwable error = context.exception();
        AgenticScope scope = context.agenticScope();

        // Log error details
        System.err.println("Error in agent: " + agentName);
        System.err.println("Error type: " + error.getClass().getSimpleName());
        System.err.println("Error message: " + error.getMessage());

        // Read state to understand context
        String input = scope.readState("data");
        System.err.println("Input that caused error: " + input);

        // Decide recovery strategy
        return ErrorRecoveryResult.throwException();
    }

    @ChatModelSupplier
    static ChatModel chatModel() {
        return new OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .build();
    }
}

Error Recovery Result

Indicates how to recover from an error (retry or throw).

/**
 * Result indicating error recovery strategy
 */
public class ErrorRecoveryResult {
    /**
     * Retry the failed operation
     * @return Recovery result indicating retry
     */
    static ErrorRecoveryResult retry();

    /**
     * Re-throw the exception (no recovery)
     * @return Recovery result indicating throw
     */
    static ErrorRecoveryResult throwException();
}

Usage Example:

import dev.langchain4j.agentic.agent.ErrorContext;
import dev.langchain4j.agentic.agent.ErrorRecoveryResult;

public interface RetryableAgent {
    @Agent(description = "Agent with retry logic", outputKey = "result")
    String process(@V("data") String data);

    @ErrorHandler
    static ErrorRecoveryResult handleError(ErrorContext context) {
        AgenticScope scope = context.agenticScope();

        // Track retry attempts
        Integer retries = scope.readState("retryCount", 0);

        if (retries < 3) {
            scope.writeState("retryCount", retries + 1);
            System.out.println("Retrying... Attempt " + (retries + 1));
            return ErrorRecoveryResult.retry();
        }

        System.err.println("Max retries exceeded, giving up");
        return ErrorRecoveryResult.throwException();
    }

    @ChatModelSupplier
    static ChatModel chatModel() {
        return new OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .build();
    }
}

Error Handling Patterns

Handling Missing Arguments

import dev.langchain4j.agentic.agent.MissingArgumentException;
import dev.langchain4j.agentic.agent.ErrorContext;
import dev.langchain4j.agentic.agent.ErrorRecoveryResult;

public interface MissingArgHandler {
    @Agent(description = "Handles missing arguments", outputKey = "result")
    String process(@V("required") String required, @V("optional") String optional);

    @ErrorHandler
    static ErrorRecoveryResult handleError(ErrorContext context) {
        if (context.exception() instanceof MissingArgumentException mEx) {
            String missingArg = mEx.argumentName();

            // Provide default for optional parameters
            if ("optional".equals(missingArg)) {
                context.agenticScope().writeState("optional", "default_value");
                return ErrorRecoveryResult.retry();
            }

            // Cannot recover from missing required parameter
            System.err.println("Required parameter missing: " + missingArg);
        }

        return ErrorRecoveryResult.throwException();
    }

    @ChatModelSupplier
    static ChatModel chatModel() {
        return new OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .build();
    }
}

Handling Timeouts

import java.util.concurrent.TimeoutException;

public interface TimeoutHandler {
    @Agent(description = "Handles timeouts", outputKey = "result")
    String process(@V("query") String query);

    @ErrorHandler
    static ErrorRecoveryResult handleError(ErrorContext context) {
        if (context.exception() instanceof TimeoutException) {
            AgenticScope scope = context.agenticScope();
            Integer timeouts = scope.readState("timeoutCount", 0);

            if (timeouts < 2) {
                scope.writeState("timeoutCount", timeouts + 1);
                System.out.println("Timeout occurred, retrying with backoff");
                // Could implement exponential backoff here
                return ErrorRecoveryResult.retry();
            }

            System.err.println("Multiple timeouts, failing");
        }

        return ErrorRecoveryResult.throwException();
    }

    @ChatModelSupplier
    static ChatModel chatModel() {
        return new OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .timeout(Duration.ofSeconds(30))
            .build();
    }
}

Fallback Strategies

public interface FallbackAgent {
    @Agent(description = "Agent with fallback", outputKey = "result")
    String process(@V("input") String input, @V("useAdvanced") boolean useAdvanced);

    @ErrorHandler
    static ErrorRecoveryResult handleError(ErrorContext context) {
        AgenticScope scope = context.agenticScope();

        // Try simpler approach on error
        Boolean useAdvanced = scope.readState("useAdvanced", true);
        if (useAdvanced) {
            System.out.println("Advanced processing failed, falling back to simple mode");
            scope.writeState("useAdvanced", false);
            return ErrorRecoveryResult.retry();
        }

        // Both approaches failed
        System.err.println("All processing strategies failed");
        scope.writeState("error", context.exception().getMessage());
        return ErrorRecoveryResult.throwException();
    }

    @ChatModelSupplier
    static ChatModel chatModel() {
        return new OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .build();
    }
}

Circuit Breaker Pattern

public interface CircuitBreakerAgent {
    @Agent(description = "Agent with circuit breaker", outputKey = "result")
    String process(@V("request") String request);

    @ErrorHandler
    static ErrorRecoveryResult handleError(ErrorContext context) {
        AgenticScope scope = context.agenticScope();

        // Track consecutive failures
        Integer consecutiveFailures = scope.readState("consecutiveFailures", 0);
        scope.writeState("consecutiveFailures", consecutiveFailures + 1);

        // Open circuit after threshold
        if (consecutiveFailures >= 5) {
            scope.writeState("circuitOpen", true);
            System.err.println("Circuit breaker opened after " + consecutiveFailures + " failures");
            return ErrorRecoveryResult.throwException();
        }

        // Check if circuit is open
        Boolean circuitOpen = scope.readState("circuitOpen", false);
        if (circuitOpen) {
            System.err.println("Circuit is open, failing fast");
            return ErrorRecoveryResult.throwException();
        }

        // Normal retry logic
        return ErrorRecoveryResult.retry();
    }

    @ChatModelSupplier
    static ChatModel chatModel() {
        return new OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .build();
    }
}

Error Logging and Monitoring

public interface MonitoredAgent {
    @Agent(description = "Agent with error monitoring", outputKey = "result")
    String process(@V("data") String data);

    @ErrorHandler
    static ErrorRecoveryResult handleError(ErrorContext context) {
        // Log comprehensive error information
        logError(context);

        // Store error in scope for later analysis
        context.agenticScope().writeState("lastError", Map.of(
            "agentName", context.agentName(),
            "errorType", context.exception().getClass().getSimpleName(),
            "errorMessage", context.exception().getMessage(),
            "timestamp", System.currentTimeMillis()
        ));

        // Send metrics/alerts (in production)
        // metricsService.incrementErrorCount(context.agentName());
        // alertingService.sendAlert(context);

        return ErrorRecoveryResult.throwException();
    }

    static void logError(ErrorContext context) {
        System.err.println("=== Agent Error ===");
        System.err.println("Agent: " + context.agentName());
        System.err.println("Exception: " + context.exception().getClass().getName());
        System.err.println("Message: " + context.exception().getMessage());
        System.err.println("Stack trace:");
        context.exception().printStackTrace();
    }

    @ChatModelSupplier
    static ChatModel chatModel() {
        return new OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .build();
    }
}

Multi-Agent Workflow Error Handling

public interface ResilientWorkflow {
    @SequenceAgent(
        outputKey = "finalResult",
        subAgents = { StepA.class, StepB.class, StepC.class }
    )
    ResultWithAgenticScope<String> execute(@V("input") String input);

    @ErrorHandler
    static ErrorRecoveryResult handleError(ErrorContext context) {
        String failedAgent = context.agentName();
        AgenticScope scope = context.agenticScope();

        System.err.println("Workflow failed at: " + failedAgent);

        // Agent-specific recovery strategies
        switch (failedAgent) {
            case "StepA":
                // Step A failures are recoverable with default data
                scope.writeState("stepAResult", "default");
                return ErrorRecoveryResult.retry();

            case "StepB":
                // Step B failures: retry once
                Integer stepBRetries = scope.readState("stepBRetries", 0);
                if (stepBRetries < 1) {
                    scope.writeState("stepBRetries", stepBRetries + 1);
                    return ErrorRecoveryResult.retry();
                }
                break;

            case "StepC":
                // Step C failures are terminal
                System.err.println("Final step failed, cannot recover");
                break;
        }

        return ErrorRecoveryResult.throwException();
    }

    @ChatModelSupplier
    static ChatModel chatModel() {
        return new OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4")
            .build();
    }
}

Exception Types

Agent Invocation Exception

Exception thrown when an error occurs during agent invocation or execution.

/**
 * Exception thrown during agent invocation or execution
 * Note: This exception does not have a getMessage() method override.
 * Access error details through the ErrorContext in @ErrorHandler methods.
 */
public class AgentInvocationException extends RuntimeException {
    /**
     * Constructor with cause
     * @param cause The underlying exception
     */
    public AgentInvocationException(Throwable cause);

    /**
     * Gets the underlying cause
     * @return The cause throwable
     */
    public Throwable getCause();
}

Package: dev.langchain4j.agentic.agent

Usage: This exception wraps other exceptions that occur during agent execution. To access error details, use the ErrorContext provided to @ErrorHandler methods rather than calling methods on the exception directly.

Example:

@ErrorHandler
static ErrorRecoveryResult handleError(ErrorContext context) {
    // Access the underlying exception through ErrorContext
    Throwable error = context.exception();

    // If it's an AgentInvocationException, get the cause
    if (error instanceof AgentInvocationException aiEx) {
        Throwable cause = aiEx.getCause();
        System.err.println("Agent invocation failed due to: " + cause.getClass().getName());

        // Handle specific causes
        if (cause instanceof TimeoutException) {
            return ErrorRecoveryResult.retry();
        }
    }

    return ErrorRecoveryResult.throwException();
}

Missing Argument Exception

Thrown when a required parameter is missing during agent execution.

/**
 * Exception thrown when a required argument is missing
 */
public class MissingArgumentException extends RuntimeException {
    /**
     * Gets the name of the missing argument
     * @return The argument name
     */
    public String argumentName();
}

Package: dev.langchain4j.agentic.agent

Usage: This exception is typically caught in @ErrorHandler methods to provide default values or alternative recovery strategies.

Example:

@ErrorHandler
static ErrorRecoveryResult handleError(ErrorContext context) {
    if (context.exception() instanceof MissingArgumentException mEx) {
        String argName = mEx.argumentName();
        System.err.println("Missing required argument: " + argName);
        
        // Provide default value
        context.agenticScope().writeState(argName, getDefaultValue(argName));
        return ErrorRecoveryResult.retry();
    }
    return ErrorRecoveryResult.throwException();
}

Install with Tessl CLI

npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-agentic@1.7.0

docs

agent-definition.md

error-handling.md

index.md

lifecycle-control.md

memory-parameters.md

multi-agent-orchestration.md

resource-suppliers.md

runtime-support.md

scope-state.md

tile.json