Skip to content

Do not use Access protocol on values that may be structs when normalizing erlang errors #14532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 26, 2025

Conversation

lukaszsamson
Copy link
Contributor

I noticed this this error in ElixirLS. I don't know what code triggered it but the ErlangError.normalize implementation doesn't seem correct in assuming that Access protocol can be used on unknown value coming from stack trace.

** (UndefinedFunctionError) function Foo.fetch_2 is undefined (Foo does not implement the Access behaviour

You can use the "struct.field" syntax to access struct fields. You can also use Access.key/1 to access struct fields dynamically inside <REDACTED: user-file-path>)
    (foo_tools 0.1.0) Foo.fetch(%Foo{direct: MapSet.new([]), transient: %{}, all: MapSet.new([])}, :transient)
    (elixir 1.17.3) <REDACTED: user-file-path>:322: Access.get/3
    (elixir 1.17.3) <REDACTED: user-file-path>:2288: ErlangError.normalize/2
    (elixir 1.17.3) <REDACTED: user-file-path>:192: Exception.blame/3

While working on that change I started wondering what is the contract on Exception.blame. Exception.message callback does use try rescue pattern so the call should be safe. blame invokes client module code with no guarantees so a faulty implementation might throw/raise. https://github.com/elixir-lang/elixir/blob/main/lib/elixir/lib/exception.ex#L214
Shouldn't blame catch the error and fall back to the provided argument values if the callback crashes? Without that guarantees blame is at least cumbersome to use as the correct way would be

try do
  some_code()
catch kind, payload ->
  {payload, stacktrace} = try do
    Exception.blame(kind, payload, __STACKTRACE__)
  catch
     _, _ ->
       {payload, __STACKTRACE__}
  end

  Exception.format(kind, payload, stacktrace)
end

@josevalim josevalim merged commit 22467ab into elixir-lang:main May 26, 2025
13 checks passed
@josevalim
Copy link
Member

💚 💙 💜 💛 ❤️

@lukaszsamson
Copy link
Contributor Author

Shouldn't blame catch the error and fall back to the provided argument values if the callback crashes?

@josevalim Any comment on that?

@josevalim
Copy link
Member

The issue is that then bugs on blame will remain uncaught. You shouldn’t need to rescue it in the long term.

@lukaszsamson
Copy link
Contributor Author

The issue is that then bugs on blame will remain uncaught.

True @josevalim. On the other hand if the error is not handled then a faulty Exception.blame callback implementation in client code/third party lib will bring my error handler code down and shadow the original error and stacktrace. I'm generally more interested in the original error and this behaviour makes debugging and fixing the original issue harder. If we keep the current behaviour I think it's worth at least documenting that Exception.blame may raise on error in callback implementation while Exception.message will recover from errors in the callback implementation. Especially since the two differ

Nezteb pushed a commit to Nezteb/elixir that referenced this pull request Jun 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants