OTP 25.0-rc1 (Release Candidate 1) is released

OTP 25.0-rc1

Erlang/OTP 25.0-rc1 is the first release candidate of three before the OTP 25.0 release.

The intention with this release is to get feedback from our users. All feedback is welcome, even if it is only to say that it works for you.
We encourage users to try it out and give us feedback either by creating an issue here Issues · erlang/otp · GitHub or by posting to Erlangforums or the mailing list erlang-questions@erlang.org.

All artifacts for the release can be downloaded from the Erlang/OTP Github release and you can view the new documentation at Erlang/OTP 25.0 Release Candidate 1. You can also install the latest release using kerl like this: kerl build 25.0-rc1 25.0-rc1.

Erlang/OTP 25 is a new major release with new features, improvements as well as a few incompatibilities. Some of the new features are highlighted below.

Many thanks to all contributors!

Below are some highlights of the release:


erts & jit

  • The JIT now works for 64-bit ARM processors.
  • The JIT now does type-based optimizations based on type
    information in the BEAM files.
  • Improved the JIT’s support for external tools like perf
    and gdb, allowing them to show line numbers and even
    the original Erlang source code when that can be found.

erts, stdlib, kernel

  • Users can now configure ETS tables with the
    {write_concurrency, auto} option. This option forces
    tables to automatically change the number of locks that
    are used at run-time depending on how much concurrency
    is detected. The {decentralized_counters, true} option
    is enabled by default when {write_concurrency, auto} is
    active.Benchmark results comparing this option with the other
    ETS optimization options are available here:
  • To enable more optimizations, BEAM files compiled with
    OTP 21 and earlier cannot be loaded in OTP 25.
  • The signal queue of a process with
    the process flag message_queue_data=off_heap has been optimized to
    allow parallel reception of signals from multiple processes.
    This can improve performance when many processes are sending in parallel to
    one process. See benchmark.
  • The Erlang installation directory is now relocatable on
    the file system given that the paths in the
    installation’s RELEASES file are paths that are
    relative to the installations root directory.
  • A new option called short has been added to the
    functions erlang:float_to_list/2 and
    erlang:float_to_binary/2. This option creates the
    shortest correctly rounded string representation of the
    given float that can be converted back to the same
    float again.
  • Introduction of quote/1 and unquote/1 functions in
    the uri_string module - a replacement for the deprecated functions http_uri:encode
    and http_uri:decode.
  • The new module peer supersedes the slave module. The
    slave module is now deprecated and will be removed in OTP 27.
  • global will now by default prevent
    overlapping partitions due to network issues. This is done by
    actively disconnecting from nodes that reports that
    they have lost connections to other nodes. This will
    cause fully connected partitions to form instead of
    leaving the network in a state with overlapping
    partitions.It is possible to turn off the new behavior by setting the
    the kernel configuration parameter prevent_overlapping_partitions to false.
    Doing this will retain the same behavior as in OTP 24 and earlier.
  • The format_status/2 callback for gen_server, gen_statem
    and gen_event has been deprecated in favor of the new
    format_status/1 callback.The new callback adds the possibility to limit and
    change many more things than the just the state.
  • The timer module has been modernized and made more
    efficient, which makes the timer server less
    susceptible to being overloaded. The timer:sleep/1
    function now accepts an arbitrarily large integer.


  • The maybe ... end construction as proposed in EEP-49
    has been implemented. It can simplify complex code
    where otherwise deeply nested cases would have to be
    used.To enable maybe, give the option {enable_feature, maybe_expr} to
    the compiler. The exact option to use will change in a coming release candidate and then it will also be possible to
    use from inside the module being compiled.
  • When a record matching or record update fails, a
    {badrecord, ExpectedRecordTag} exception used to be
    raised. In this release, the exception has been changed
    to {badrecord, ActualValue}, where ActualValue is the
    value that was found instead of the expected record.
  • Add compile attribute -nifs() to empower compiler and loader with
    information about which functions may be overridden as NIFs by erlang:load_nif/2.
  • Improved and more detailed error messages when binary construction with the
    binary syntax fails.
    This applies both for error messages in the shell and for


  • Add crypto:hash_equals/2 which is a constant time comparision of hashvalues.


  • Optimize operations in the erl_types module. Parallelize the Dialyzer pass remote.
  • Added the missing_return and extra_return options to
    raise warnings when specifications differ from inferred
    types. These are similar to, but not quite as verbose
    as overspecs and underspecs.
  • Dialyzer now better understands the types for min/2,
    max/2, and erlang:raise/3. Because of that, Dialyzer
    can potentially generate new warnings. In particular,
    functions that use erlang:raise/3 could now need a spec
    with a no_return() return type to avoid an unwanted


  • A new DEVELOPMENT HOWTO guide has been added that
    describes how to build and test Erlang/OTP when fixing
    bugs or developing new functionality.
  • Testing has been
    added to the Github actions run for each opened PR so
    that more bugs are caught earlier when bug fixes and
    new features are proposed.

For more details about new features and potential incompatibilities see


Congrats on the new release Erlang/OTP team!

I have one question: I was reading the benchmarks for the new {write_concurrency, auto} option and my understanding is that it outperforms {write_concurrency, true} on all scenarios.

If that is the case, would it make sense to make {write_concurrency, true} actually mean {write_concurrency, auto} and have an option called {write_concurrency, static} (or similar) for the current mode of operation?

Assuming that auto outperforms true on all scenarios, the benefits for having true actually meaning to auto would be:

  1. All existing code can benefit from those improvements without any source code changes

  2. There may be no reasons for true to exist in the future. So moving the current behaviour to {write_concurrency, static} may make it easier to replace or deprecate it in the future. Perhaps the only reason someone would use {write_concurrency, static} is if there are bugs in the current auto implementation?

Or perhaps my assumptions are wrong! :smiley: In any case, this looks great! Thanks!


auto outperforms true in all performance scenarios (we have tried), but it uses more memory to do so. auto also has some unique problems when it comes to shrinking of the table, we have attempted to fix them, but it remains an unsolved problem where some scenarios result in much higher memory usage of a table that has grown and then shrunk.

We did consider just changing true to have the new behavior but have for now decided to take the conservative route.


Just reporting that Yaws 2.1.1 builds and passes all tests with 25.0-rc1.


Elixir test suite is also green on Erlang/OTP 25.0-rc1!


Prompted by some comments in the Elixir slack I installed OTP 25.0-rc1 on my M1 Max MacBook Pro with ASDF.

I recompiled some of my larger projects and ran their tests - amazing performance difference to the non-JIT-for-ARM OTP 24. No scientific testing data, but its definitely a material difference in performance. Amazing work by the OTP team. Looking forward to the official release!


Got the strangest issue, trying to compile MongooseIM, it error-ed out at compiling one of our deps: GitHub - mysql-otp/mysql-otp: MySQL/OTP – MySQL and MariaDB client for Erlang/OTP

Compiling mysql-otp directly runs the same error, just fetch the repo and run make:

mysql:1: function transaction/4+27:
  Internal consistency check failed - please report this bug.
  Instruction: {call_last,4,{f,164},4}
  Error:       {bad_arg_type,{x,1},any,{t_fun,any,any,any}}:

Thanks for your report! We fixed a similar issue last week, can you try latest master?


Right, just built master, both mysql-otp and in general MongooseIM build correctly, thanks!