CVE-2026-77950 Overview
CVE-2026-77950 is an information disclosure vulnerability in the ash_typescript library maintained by the ash-project. The flaw exists in the apply_error_handler/3 function in lib/ash_typescript/rpc/errors.ex. When a configured error handler raises a FunctionClauseError due to an unmatched error shape, the rescue clause returns the original, pre-handler error map to the client. This behavior leaks internal error data, including secrets carried in error vars, to unauthenticated remote attackers. The vulnerability affects ash_typescript versions from 0.8.0 before 0.18.0 and is tracked under [CWE-209: Generation of Error Message Containing Sensitive Information].
Critical Impact
An unauthenticated attacker can provoke arbitrary error shapes over the RPC boundary to receive unredacted internal error data, including sensitive variables the application intended to suppress.
Affected Products
- ash-project ash_typescript versions 0.8.0 through 0.17.x
- Elixir applications exposing Ash RPC endpoints via ash_typescript
- Applications relying on custom error handlers for redaction or suppression
Discovery Timeline
- 2026-09-01 - CVE CVE-2026-77950 published to NVD
- 2026-09-01 - Last updated in NVD database
Technical Details for CVE-2026-77950
Vulnerability Analysis
The apply_error_handler/3 function is the sole hook applications have for redacting or suppressing errors before they reach the client. A nil return from the handler drops the error entirely. Error handlers are conventionally implemented as pattern-matching functions that match expected error shapes.
When an unexpected error shape reaches the handler, no clause matches. Elixir raises a FunctionClauseError. The surrounding rescue clause logs a warning and then returns the original, pre-handler error map. The intent to suppress an error inverts into an intent to publish it. Any secrets carried in the error vars field flow directly to the unauthenticated client.
The rescue clause catches exceptions only. A handler that uses throw or exit still propagates uncaught, breaking the RPC response path in a different but related failure mode.
Root Cause
The root cause is fail-open error handling. The rescue path treated a handler crash as a recoverable event and fell back to the raw error, rather than treating the crash as a signal that redaction had failed. This design choice conflates handler-implementation bugs with runtime noise, and it violates the principle that a sanitization component must fail closed [CWE-209].
Attack Vector
An unauthenticated remote attacker sends RPC requests crafted to trigger internal error paths that produce error shapes the application's handler does not match. The attacker does not need to know handler internals; iterating request shapes is sufficient to induce mismatches. Each mismatch returns the raw transformed error, including internal variables, in the RPC response.
end
rescue
e ->
- Logger.warning("""
- Error handler failed: #{inspect(e)}
- Handler: #{inspect({module, function, args})}
- Original error: #{inspect(error)}
- """)
+ handler_failure(inspect(e), __STACKTRACE__, {module, function, args}, error)
+ catch
+ kind, reason ->
+ handler_failure(
+ "#{kind}: #{inspect(reason)}",
+ __STACKTRACE__,
+ {module, function, args},
+ error
+ )
+ end
+
+ # Error handlers are the application's hook for redacting or suppressing errors
+ # before they reach the client, so a handler that fails must fail closed - never
+ # fall back to the unredacted error it was supposed to sanitize.
+ defp handler_failure(reason, stacktrace, handler, error) do
+ uuid = Ash.UUID.generate()
+
+ Logger.error("""
+ Error handler failed, returning a generic error instead of the unhandled one.
+ Error ID: #{uuid}
+ Handler: #{inspect(handler)}
+ Failure: #{reason}
Source: GitHub Commit 59d8e985 - fix(rpc): fail closed when a configured error handler crashes
The patch replaces the fall-back to the original error with a handler_failure/4 helper that emits a generic error identifier and logs the failure server-side. It also adds a catch clause to intercept throw and exit cases.
Detection Methods for CVE-2026-77950
Indicators of Compromise
- RPC responses returning error payloads containing internal atom keys, module names, or stack-trace fragments not normally exposed by the configured handler.
- Server logs containing repeated FunctionClauseError entries originating from the application's Ash error handler module.
- Anomalous spikes in RPC 4xx or 5xx responses correlated with unusual client input shapes.
Detection Strategies
- Inventory Elixir applications using ash_typescript and confirm versions against the affected range >= 0.8.0, < 0.18.0.
- Add server-side alerts on Logger.warning entries matching the string Error handler failed, which is emitted by the vulnerable rescue path.
- Review outbound RPC error responses for leakage patterns such as vars, internal identifiers, or credentials-like strings.
Monitoring Recommendations
- Instrument the Ash RPC layer to record when error handlers raise, exit, or throw, and treat any occurrence as a security event.
- Correlate unauthenticated request sources with elevated handler-failure rates to identify enumeration behavior.
- Retain full request and response bodies for RPC error paths long enough to support post-incident analysis.
How to Mitigate CVE-2026-77950
Immediate Actions Required
- Upgrade ash_typescript to version 0.18.0 or later across all deployed Elixir services.
- Audit any custom error handler module for non-exhaustive pattern matching and add a catch-all clause returning a generic error.
- Rotate any secrets that may have been carried in error vars and exposed through RPC responses during the vulnerable window.
Patch Information
The fix landed in commit 59d8e985a98cf2e01794dbe5b919b897a95311f4 and is released in ash_typescript0.18.0. See the GitHub Security Advisory GHSA-mjr7-r3rf-x963, the CNA advisory for CVE-2026-77950, and the OSV record EEF-CVE-2026-77950 for full details.
Workarounds
- Add a catch-all clause to the configured error handler that returns a static, generic error map for any unmatched input shape.
- Wrap the handler body in an explicit try/rescue/catch that returns a redacted error for exceptions, throws, and exits alike.
- Strip or exclude the vars field from all outbound error payloads at the RPC boundary until the upgrade is deployed.
# Update the dependency in mix.exs and fetch the patched release
# mix.exs
# {:ash_typescript, "~> 0.18.0"}
mix deps.update ash_typescript
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.