Skip to content
tnsaiDocs
GitHub

Exceptions

Most TnsAI exceptions extend TnsAIException, an unchecked exception that says whether a retry can help and how long to wait. An action call that fails throws ActionExecutionException, whose category names the cause.

TnsAIException

MethodReturns
isRetryable()true for transient failures such as network errors, rate limits and server errors
getErrorCode()TNSAI- plus the class name without Exception, upper-cased: TNSAI-NETWORK, TNSAI-RATELIMIT
getSuggestedRetryDelayMs()1000 when retryable and 0 otherwise, unless a subclass sets its own
getMaxRetryAttempts()3 when retryable and 0 otherwise, unless a subclass sets its own

The error code is stable, so it works as a metric or log tag.

ActionExecutionException

An action call that fails throws ActionExecutionException. It carries getActionName(), getActionType(), getCategory() and getDetailedMessage(), a one-line summary for logs.

CategoryRetryableCause
NETWORKYesConnection timeout, DNS failure
PARAMETERNoMissing parameter, wrong type
CLIENT_ERRORNoHTTP 4xx response
SERVER_ERRORYesHTTP 5xx response
VALIDATIONNoA failed contract, guardrail or validation check
LLMYesModel error, quota exceeded
MCPYesMCP tool error
INVOCATIONNoReflection failure, method not found
UNKNOWNNoAnything else

Two cases land in VALIDATION:

  • A failed @Contract clause. The cause is a ContractViolationException with getContractKind(), getViolatedExpression() and getBindings(). See Contracts.
  • An LLM action on an agent with no LLMClient. The call fails instead of returning null, so @Fallback can run. See LLM actions.

ApprovalRequiredException and security exceptions are not wrapped. They reach the caller as they are.

LLMException

A failed model call throws LLMException. getErrorType() names the cause, and getModel() the model.

ErrorTypeRetryableCause
MODEL_NOT_FOUNDNoModel not found or unavailable
AUTHENTICATION_FAILEDNoInvalid API key
CONTENT_FILTEREDNoContent policy violation
INVALID_REQUESTNoInvalid request
MODEL_OVERLOADEDYesModel overloaded; suggested delay 5000 ms
CONTEXT_TOO_LONGNoContext length exceeded
SERVER_ERRORYesProvider server error; suggested delay 2000 ms
MALFORMED_TOOL_CALLNoThe model sent bad JSON or missed a field in a tool call
CAPABILITY_MISMATCHNoThe model lacks a required capability
UNKNOWNYesAnything else

LLMCapabilityException extends LLMException for a feature the model or provider does not support, such as streaming, vision or structured output. ToolCallNotSupportedException extends it for tool calls.

Other exceptions

ExceptionRetryableFields
RateLimitExceptionYes, up to 5 times. The delay is the provider's Retry-After, or 60000 ms.getRetryAfterMs(), getService(), getRemainingQuota()
NetworkExceptionYeshost and port
TimeoutExceptionYestimeout and operation
ValidationExceptionNo
KnowledgeNotReadyExceptionNo. It extends ValidationException.The host class, the source and the Problem: MISSING, EMPTY or UNREADABLE
ApprovalRequiredExceptionNoaction name and reason
TaskCompleteExceptionNoNot implemented: no framework loop catches it, and @CompletionSignal is not read.

Handle a failure

Choose a recovery from the category of an action failure. For a chat turn, catch the most specific type first.

package com.example.tnsai.docs;

import com.tnsai.agents.Agent;
import com.tnsai.exceptions.ActionExecutionException;
import com.tnsai.exceptions.LLMException;
import com.tnsai.exceptions.RateLimitException;
import com.tnsai.exceptions.TnsAIException;

import java.util.Map;

public final class ExceptionsExample {
    private ExceptionsExample() {}

    // An action call: choose a recovery from the category.
    static Object forecast(Agent agent) {
        try {
            return agent.executeAction("forecast", Map.of("city", "Paris"));
        } catch (ActionExecutionException e) {
            System.err.println(e.getDetailedMessage());
            return switch (e.getCategory()) {
                case NETWORK, SERVER_ERROR -> "retry in " + e.getSuggestedRetryDelayMs() + " ms";
                case PARAMETER, VALIDATION -> "fix the arguments for " + e.getActionName();
                default -> throw e;
            };
        }
    }

    // A chat turn: catch the most specific type first.
    static String ask(Agent agent, String question) {
        try {
            return agent.chat(question);
        } catch (RateLimitException e) {
            return "rate limited, retry in " + e.getSuggestedRetryDelayMs() + " ms";
        } catch (LLMException e) {
            if (e.getErrorType() == LLMException.ErrorType.CONTEXT_TOO_LONG) {
                return "shorten the question";
            }
            throw e;
        } catch (TnsAIException e) {
            System.err.println(e.getErrorCode() + " retryable=" + e.isRetryable());
            throw e;
        }
    }
}

To retry inside the framework instead, see Resilience.

  • Actions: the steps where an action call can fail.
  • Contracts: the clauses behind VALIDATION.
  • Resilience: retries, timeouts and fallbacks.
  • Knowledge: sources that raise KnowledgeNotReadyException.