CVE-2026-82580 Overview
CVE-2026-82580 is an information disclosure vulnerability in the ash-project/ash_ai library, an AI tooling framework for the Elixir Ash ecosystem. The flaw exists in AshAi.ToolLoop and AshAi.Tools, where exceptions raised during tool execution were serialized verbatim using Exception.message/1 and returned to the language model as tool-result content. The model then typically relays that content to the chat user. A user who can influence tool arguments into triggering a raised exception receives raw internal error text, including database constraint messages, adapter errors, query fragments, and policy or validation internals. The issue is tracked as [CWE-209: Generation of Error Message Containing Sensitive Information].
Critical Impact
Chat users can extract internal application details such as database schema, SQL fragments, and policy internals by steering tool arguments into code paths that raise exceptions.
Affected Products
- ash_ai versions from 0.6.0 up to (but not including) 1.0.0
- Applications embedding AshAi.ToolLoop for chat tool execution
- Applications embedding AshAi.Tools for tool result normalization
Discovery Timeline
- 2026-08-31 - CVE-2026-82580 published to NVD
- 2026-09-01 - Last updated in NVD database
Technical Details for CVE-2026-82580
Vulnerability Analysis
The ash_ai library integrates Ash resources with large language models by exposing them as callable tools. When a tool callback raised an Elixir exception, the library caught the exception in a rescue clause and encoded Exception.message(e) directly into the JSON error payload returned to the model. That payload was appended to the conversation, emitted as a {:tool_result, ...} stream event, and sent back to the model in the next turn. The model then commonly surfaces that text to the end user in a chat response.
Because Elixir exceptions frequently embed contextual detail, the exposed strings can include Ecto changeset constraint text, adapter-level SQL errors, Ash policy failure explanations, and internal query fragments. Any actor able to shape tool inputs into a raising code path effectively gains a read primitive over server-side error internals [CWE-209].
Root Cause
Both lib/ash_ai/tool_loop.ex and lib/ash_ai/tools.ex used Exception.message/1 without passing exceptions through the safe formatter AshAi.Tool.Errors.format/1 already used elsewhere in the codebase for known tool errors. Unknown or unexpected exceptions therefore bypassed sanitization entirely.
Attack Vector
Exploitation requires an authenticated chat user with the ability to supply arguments to a tool. The attacker sends prompts that cause the model to invoke a tool with values likely to trigger exceptions, for example values that violate database constraints, fail authorization policies, or cause adapter parsing errors. The raised exception message is echoed back through the conversation, giving the attacker structured leakage of backend internals.
// Security patch in lib/ash_ai/tool_loop.ex
fun.(args, ctx)
rescue
e ->
- {:error, Jason.encode!(%{error: Exception.message(e)})}
+ # Route the raised exception through the same safe formatter as
+ # other tool errors instead of echoing Exception.message/1,
+ # which can carry internal details (DB schema, SQL, policy
+ # internals). Unknown errors are logged and rendered generically.
+ {:error, Jason.encode!(%{error: AshAi.Tool.Errors.format(e)})}
end
{:error, reason} ->
Source: GitHub Commit c68dfbd
// Security patch in lib/ash_ai/tools.ex
defp normalize_extra_tool_error(content) do
- Jason.encode!(%{error: Exception.message(content)})
+ # Route through the same safe formatter as other tool errors instead of
+ # echoing Exception.message/1, which can carry internal details (DB schema,
+ # SQL, policy internals). Unknown errors are logged and rendered generically.
+ Jason.encode!(%{error: AshAi.Tool.Errors.format(content)})
rescue
- _ -> Jason.encode!(%{error: inspect(content)})
+ _ -> Jason.encode!(%{error: "Tool execution failed"})
end
Source: GitHub Commit c68dfbd
Detection Methods for CVE-2026-82580
Indicators of Compromise
- Chat transcripts or {:tool_result, ...} stream events containing raw Elixir exception text, module names like Ecto.ConstraintError, or SQL fragments.
- Tool result JSON payloads whose error field contains stack-relevant strings such as table names, column names, or Ash.Error.* internals.
- Repeated tool invocations from the same session designed to trigger validation, constraint, or policy failures.
Detection Strategies
- Inspect stored conversation history for JSON tool results matching patterns like {"error":"** (Ecto or {"error":"** (Ash.Error.
- Add log-side allowlists that flag tool result payloads exceeding an expected size or containing database identifiers.
- Correlate a spike in tool error rates per session with the same user identifier to surface probing behavior.
Monitoring Recommendations
- Instrument AshAi.ToolLoop with telemetry events for tool exceptions and forward them to a centralized logging pipeline.
- Track the ratio of successful to failed tool invocations per user and alert on outliers.
- Retain full request and response traces from LLM providers to enable retrospective analysis if leakage is suspected.
How to Mitigate CVE-2026-82580
Immediate Actions Required
- Upgrade ash_ai to version 1.0.0 or later, which routes raised tool errors through AshAi.Tool.Errors.format/1.
- Audit existing conversation logs for leaked internal error content and rotate any credentials or schema details that may have been exposed.
- Review custom tool implementations to ensure they do not independently serialize Exception.message/1 into tool results.
Patch Information
The fix is delivered in commit c68dfbdb220426dc015b58379e95bde8e9f0995a and published in ash_ai 1.0.0. Both AshAi.ToolLoop and AshAi.Tools now format unknown exceptions with the shared safe formatter and fall back to the generic string "Tool execution failed" when formatting itself raises. See the GitHub Security Advisory GHSA-5747-6mpw-6m9c and the CNA advisory for CVE-2026-82580 for the full patch record.
Workarounds
- Wrap tool callbacks in application-level try/rescue blocks that return sanitized error strings before returning control to AshAi.
- Add a post-processing step over {:tool_result, ...} stream events that strips or replaces content matching known Elixir or Ecto exception prefixes.
- Restrict which Ash resources and actions are exposed as tools to those with well-defined, non-sensitive failure modes.
# Pin a fixed ash_ai version in mix.exs
# {:ash_ai, "~> 1.0"}
mix deps.update ash_ai
mix deps.get
mix compile --force
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

