PraisonAIError with category, message, and run context for recovery.
Quick Start
1
Catch any agent error
2
Handle tool errors specifically
3
Catch a failed LLM tool-calling loop
How It Works
Every structured error carriesmessage, agent_id, run_id, error_category, and is_retryable. Subclasses add domain fields such as tool_name or model_name.
error_category uses typed kinds such as rate_limit, auth, context_overflow, and billing.
LLMResponseError is raised by LLM.get_response() when the tool-calling loop fails and cannot produce a response — previously this was swallowed and returned as an empty string. Catch it to distinguish tool-loop failures; a try/except Exception already covers it. See LLMResponseError.
LLM Response Errors
LLMResponseError is raised when the LLM tool-calling loop hits an exception it cannot recover from. It lives in praisonaiagents.llm alongside the other LLM exceptions:
LLMResponseError carries a message attribute describing the iteration that failed and chains the original exception via raise … from e, so e.__cause__ holds the underlying error.
Tool Failure Behavior
On the sync path, a tool failure resolves down one of three branches:- Tool raises an exception → the framework wraps it as
ToolExecutionErrorand propagates it. - Tool returns
{"error": "..."}→ the error dict is handed back to the LLM as a normal tool result, so the model can self-correct (new behavior since PR #4462, confirmed in #4470 which fixes issue #4446. The generic-exception branch inopenai_client.pybehaves the same way.). - Tool returns a retryable-transient / denial dict →
ToolExecutionErrorwithis_retryable=True, or a short-circuit denial.
Behavior change (PraisonAI PR #4462). A tool that returns
{"error": "..."} no longer aborts the run — this was the convention already used by bundled tools like execute_command, tavily_search, and exa_search. Previously the framework raised ToolExecutionError(is_retryable=False) for any dict with a truthy error key, so an empty-command warning from execute_command or a missing-API-key message from a search tool would kill agent.chat() mid-turn. Now the LLM sees the error dict as a normal tool result and can self-correct (retry with a real command, ask the user for the missing key, etc.).Only three cases still raise / short-circuit on the sync path:- The tool raised an exception (framework-level failure).
- The dict is a denial (
approval_denied,permission_denied,approval_error,policy_denied,guardrail_denied). - The dict is a retryable transient (
_outer_timeouton an idempotent tool,circuit_open, or a_praison_retryablemarker outside the tool’s retry policy).
chat() / start() calls in try / except ToolExecutionError to catch a raised tool exception:
{"error": ...} dicts — both hand the error back to the LLM so it can retry with corrected arguments. The two paths still differ for raised exceptions: sync chat() raises ToolExecutionError; async achat() returns an error dict. See Async Tool Safety.
Common Patterns
Retry on transient network failures, fail on config bugs:Reaching the Step Limit
When the tool-calling loop reachesExecutionConfig.max_steps (or max_iter when max_steps is unset), the agent does not hard-cut. On the final permitted step it injects a graceful wrap-up instruction, so the model returns a real summary of what it accomplished and what remains — not a placeholder.
Detect truncation with agent.last_stop_reason instead of string-matching:
praisonai run / praisonai-code run, the wrapper reports it distinctly from success — exit code 2 (not 0), status: "truncated" under --output json / --output stream-json with the wrap-up summary preserved in result, plus a one-line stderr warning in interactive mode. See praisonai run → Exit Codes.
Step Budget
Cap tool-use steps and detect graceful truncation with
last_stop_reasonBest Practices
Catch the most specific class you can handle
Catch the most specific class you can handle
Use
ToolExecutionError when you only care about tool failures; reserve PraisonAIError for top-level logging.Log structured context
Log structured context
Include
e.error_category, e.agent_id, and e.run_id in observability hooks — they correlate across multi-agent runs.Don't swallow ValidationError
Don't swallow ValidationError
Validation failures usually mean a programming or config bug. Fix the root cause instead of retrying blindly.
Pair with loop guard for retry loops
Pair with loop guard for retry loops
Loop-guard
HALT raises ToolExecutionError. Combine with Loop Guard when tools may repeat indefinitely.Treat an LLMResponseError as a real failure, not an empty answer
Treat an LLMResponseError as a real failure, not an empty answer
A failed tool-calling loop now raises
LLMResponseError instead of returning "". Catch it explicitly (from praisonaiagents.llm import LLMResponseError) so retries and observability see the actual error rather than a silent empty string.Related
Loop Guard
Stop runaway tool loops with HALT/WARN/BLOCK
Non-Fatal Errors
Callback failures captured without crashing

