Erlang/OTP 26.0-rc1 (Release Candidate 1) is released

Erlang/OTP 26.0-rc1 is the first release candidate of three before the OTP 26.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.

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

Erlang/OTP 26 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:


The Shell

There are a lot of new features and improvements in the Erlang shell:

  • auto-complete of variables, record names, record fields names, map keys, function parameter types and filenames.
  • Open external editor in the shell to edit
    the current expression.
  • defining records (with types), functions, specs and types in the shell.

New terminal

  • The TTY/terminal subsystem has been rewritten.
    Windows users will notice that erl.exe has the same
    functionality as a normal Unix shell and that werl.exe is just a
    symlink to erl.exe. This makes the Windows Erlang terminal
    experience identical to that of Unix.

Compiler and JIT optimizations:

  • Creation and matching of binaries with segments of fixed sizes have been optimized.

  • Creation and matching of UTF-8 segments have been optimized.

  • Appending to binaries has been optimized.

  • The compiler and JIT now generate better code for creation of small maps where all keys
    are literals known at compile time.

  • Thanks to the optimizations above the performance of the base64 module has been
    significantly improved. For example, on an x86_64 system with the JIT both encode and
    decode are almost three times faster than in Erlang/OTP 25.


  • Map comprehensions as suggested in EEP 58 has now been

  • Some map operations have been optimized by changing the
    internal sort order of atom keys. This changes the
    (undocumented) order of how atom keys in small maps are
    printed and returned by maps:to_list/1 and maps:next/1.
    The new order is unpredictable and may change between
    different invocations of the Erlang VM.

  • Introducing the new function maps:iterator/2 for creating an interator
    that return the map elements in a deterministic order.
    There are also new modifiers k and K for the format
    string in io:format() to support printing map elements


  • Dialyzer has a new incremental mode that be invoked by
    giving the --incremental option when running Dialyzer.
    This new incremental mode is likely to become the
    default in a future release.

Misc ERTS, Stdlib, Kernel, Compiler

  • Multi time warp mode is now enabled by default.
    This assumes that all code executing on the system is
    time warp safe.

  • Support for UTF-8 atoms and strings in the NIF
    interface including new functions enif_make_new_atom,
    enif_make_new_atom_len and enif_get_string_length.

  • The BIFs min/2 and max/2 are now allowed to be used in
    guards and match specs.

  • Improved the selective receive optimization, which can
    now be enabled for references returned from other
    functions. This greatly improves the performance of
    gen_server:send_request/3, gen_server:wait_response/2,
    and similar functions.

  • New trace feature call_memory. Similar to call_time
    tracing, but instead of measure accumulated time in
    traced functions it measures accumulated heap space
    consumed by traced functions. It can be used to compare
    how much different functions are contributing to
    garbage collection being triggered.

  • It is no longer necessary to enable a feature in the
    runtime system in order to load modules that are using
    it. It is sufficient to enable the feature in the
    compiler when compiling it.

  • inet:setopts/2 has got 3 new options: reuseport, reuseport_lb and exclusiveaddruse.

  • Fix so that -fno-omit-frame-pointer is applied to all
    of the Erlang VM when using the JIT so that tools, such
    as perf, can crawl the process stacks.

  • In the lists module, the zip family of functions now takes
    options to allow handling lists of different lengths.

  • Added the zip:zip_get_crc32/2 function to retrieve the
    CRC32 checksum from an opened ZIP archive.
    gen_server optimized by caching callback functions

  • The modules Erlang DNS resolver inet_res and helper
    modules have been updated for RFC6891; to handle OPT RR
    with DNSSEC OK (DO) bit.

  • Introduced application:get_supervisor/1.

  • Cache OTP boot code paths, to limit how many folders
    that are being accessed during a module lookup. Can be
    disabled with -cache_boot_path false.


  • Support for Kernel TLS (kTLS), has been added to the
    SSL application, for TLS distribution (-proto_dist
    inet_tls), the SSL option {ktls, true}.

  • Improved error checking and handling of ssl options.

  • Mitigate memory usage from large certificate chains by
    lowering the maximum handshake size. This should not
    effect the common cases, if needed it can be configured
    to a higher value.

  • For security reasons the SHA1 and DSA
    algorithms are no longer among the default values.

  • Add encoding and decoding of use_srtp hello extension
    to facilitate for DTLS users to implement SRTP

For more details about new features and potential incompatibilities see the readme


it now maybe enabled by default?

1 Like

maybe is now enabled by default in the runtime system, but not in the compiler. Lifting the restriction that features needed to be enabled both in the compiler and the runtime system means that we now can use in maybe in our code in Erlang/OTP.


thanks! probably it’s worth to add this information also in the changelog

1 Like

It is mentioned in the README file:

  OTP-18445    Application(s): erts, stdlib

               It is no longer necessary to enable a feature in the
               runtime system in order to load modules that are using
               it. It is sufficient to enable the feature in the
               compiler when compiling it.

I tried to build the documentation following the instructions at Documentation · erlang/otp Wiki · GitHub, the make release_docs command results in this error message:

[INFO] FOUserAgent - Rendered page #397.
[INFO] FOUserAgent - Rendered page #398.
[INFO] FOUserAgent - Rendered page #399.
/usr/bin/install -c -d “/home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/release/x86_64-pc-linux-gnu/doc/pdf”
/usr/bin/install -c -m 644
…/pdf/otp-system-documentation-14.0.pdf “/home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/release/x86_64-pc-linux-gnu/doc/pdf”
make[3]: *** No rule to make target '/home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/release/x86_64-pc-linux-gnu/doc/temporary/index.html /home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/release/x86_64-pc-linux-gnu/doc/temporary/applications.html /home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/release/x86_64-pc-linux-gnu/doc/temporary/erlresolvelinks.js /home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/release/x86_64-pc-linux-gnu/doc/temporary/man_index.html', needed by ‘html’. Stop.
make[3]: Leaving directory ‘/home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/system/doc/top’
make[2]: *** [/home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/make/ release_docs] Error 2
make[2]: Leaving directory ‘/home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/system/doc/top’
make[1]: *** [/home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/make/ release_docs] Error 2
make[1]: Leaving directory ‘/home/nar/.kerl/builds/26.0-rc1/otp_src_26.0-rc1/system/doc’
make: *** [Makefile:440: release_docs] Error 2

I’m trying to build on Ubuntu 22.04.1 LTS on x86_64. Am I missing some something? Other than error : xmlAddEntity: invalid redeclaration of predefined entity messages I haven’t seen errors in the output. I can build the documentation of OTP (this version was at hand :slight_smile: ).

1 Like

The problem seem to be version 4.3 of GNU make which is included in that version of Ubuntu.

We are currently testing this fix in our daily builds:

diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile
index 4ada828680..a39d6a904f 100644
--- a/system/doc/top/Makefile
+++ b/system/doc/top/Makefile
@@ -102,9 +102,7 @@ PDFREFDIR= pdf
-sp :=
-sp +=
+sp := ' '
 ## qs translates ' ' to '\ ', sq translates '\ ' to ' '
 ## These function are used when the make target is a path that can
1 Like

It seems the shell doesn’t add input captured via io:get_line/1 to the shell history anymore.
Is this by design? (Since it breaks the history for iex)

1 Like

Make is a terrible thing :slight_smile:

Unfortunately, sp := ' ' produces a value that contain two literal quotes. For example, this makefile:

sp := ' '
all: ; echo "$(sp)"

will produce the following output:

echo "' '"
' '

Thus it may fix the problem depending on shell quoting :slight_smile:

Seems like there are only two ways to get literal space in make variable: enclose a space within empty variables or use a trick with subst call.

1 Like

Can you not just use

s := 
space := $s $s

Using your Makefile example

s := 
space := $s $s

all: ; echo "$(space)"
leonard@captmarvel:/tmp/mt$ make all
echo " "
1 Like

Thanks, it seems to have solved my problem.

1 Like

I think OTP-18210 should be better enlightened with a “Potential incompatibility” or a better warning. As it is can cause production issue not detected on testing environments (as they are generally not exactly the same).

1 Like

The new shell is cool!

1> erlang:group_leader(
erlang:group_leader(GroupLeader, Pid)

if it’s nicer that shell telling me there is an erlang:group_leader/0 too


Im getting this error when launching erl.exe (or werl.exe).
Im running windows 7 64bits with latest windows updates on a virtualbox machine:

Microsoft Windows [Versión 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. Reservados todos los derechos.

C:\Windows\System32>"C:\Program Files\Erlang OTP\bin\erl.exe"
=ERROR REPORT==== 23-Feb-2023::15:43:45.296000 ===
** State machine user_drv terminating
** When server state  = {undefined,undefined}
** Reason for termination = error:{badmatch,
                                     'El par+ímetro no es correcto.\r\n'}}}
** Callback modules = [user_drv]
** Callback mode = state_functions
** Stacktrace =
**  [{prim_tty,init,1,[{file,"prim_tty.erl"},{line,207}]},

=CRASH REPORT==== 23-Feb-2023::15:43:45.296000 ===
    initial call: user_drv:init/1
    pid: <0.65.0>
    registered_name: []
    exception error: no match of right hand side value {error,
                                                         'El par+ímetro no es co
      in function  prim_tty:init/1 (prim_tty.erl, line 207)
      in call from user_drv:init/1 (user_drv.erl, line 151)
      in call from gen_statem:init_it/6 (gen_statem.erl, line 1022)
    ancestors: [<0.64.0>,kernel_sup,<0.47.0>]
    message_queue_len: 0
    messages: []
    links: []
    dictionary: []
    trap_exit: true
    status: running
    heap_size: 610
    stack_size: 28
    reductions: 422

=SUPERVISOR REPORT==== 23-Feb-2023::15:43:56.234000 ===
    supervisor: {local,kernel_sup}
    errorContext: start_error
    reason: nouser
    offender: [{pid,undefined},

=CRASH REPORT==== 23-Feb-2023::15:43:56.234000 ===
    initial call: supervisor_bridge:user_sup/1
    pid: <0.64.0>
    registered_name: []
    exception exit: nouser
      in function  gen_server:init_it/6 (gen_server.erl, line 938)
    ancestors: [kernel_sup,<0.47.0>]
    message_queue_len: 0
    messages: []
    links: [<0.49.0>]
    dictionary: []
    trap_exit: true
    status: running
    heap_size: 610
    stack_size: 28
    reductions: 547

=CRASH REPORT==== 23-Feb-2023::15:43:56.281000 ===
    initial call: application_master:init/4
    pid: <0.46.0>
    registered_name: []
    exception exit: {{shutdown,{failed_to_start_child,user,nouser}},
      in function  application_master:init/4 (application_master.erl, line 142)
    ancestors: [<0.45.0>]
    message_queue_len: 1
    messages: [{'EXIT',<0.47.0>,normal}]
    links: [<0.45.0>,<0.44.0>]
    dictionary: []
    trap_exit: true
    status: running
    heap_size: 376
    stack_size: 28
    reductions: 168

=INFO REPORT==== 23-Feb-2023::15:43:56.281000 ===
    application: kernel
    exited: {{shutdown,{failed_to_start_child,user,nouser}},
    type: permanent

Kernel pid terminated (application_controller) ({application_start_failure,kerne

Crash dump is being written to: erl_crash.dump...
1 Like

Why are you using an OS that is 13 years old, and out of support since 3 years ago?

I mean we can’t support old OS’s forever, than we can never have any new good stuff.


Fair enough… I was just testing the RC on the first Windows machine I had at hand. No real usage in my case.

Just for the record, OTP 25 is the last version compatible with the ancient windows 7.

1 Like

I’m currently looking over the code changes in Add support for `dialyzer --incremental` when available by TD5 · Pull Request #2736 · erlang/rebar3 · GitHub when applied to OTP-26-rc1 and I’m getting massive test failures, mostly because every single run of Dialyzer with the incremental PLT ends up mixing up the project file with the base PLT’s data (it’s a single in-project file).

I am not sure if the problem is with the PR we had received not fitting the feature in spirit, or if the implementation itself is what is lacking. So what should be the behavior here:

  1. Dialyzer in incremental mode expects one PLT file per project and that file is fully managed by Dialyzer
  2. Dialyzer can work with a big global file shared by all projects and maintains all related updates based on each run
  3. Rebar3 can store a global base incremental PLT file, and then use it as a foundation for a per-project file that once created keeps running

What we currently have in the PR for incremental mode is option 1. What Rebar3 did with classical PLTs was in option 3, and saved cross-project build time. It appears that Dialyzer can tolerate receiving an input PLT and an output PLT for incremental builds, which means option 3 ought to be supported, but I don’t want to work at cross purposes with the tools.

Either way, as we have it, the PR has tests that work as if 3 was supported but the implementation of 1, so I’m wondering what I have to do here to bring this up to spec.

1 Like

I think I actually got the latter to work (#3), I’ll just have to fix the tests. It’s less gnarly than I thought it would be.

Here’s the ongoing WIP to use the shared incremental PLT, it ought to save many minutes runs when starting a new project when another one had built the base PLT already.

1 Like

Thanks… I was trying to compile zotonic with rc1 and ran into a problem.

It looks like it is no longer possible to eval things like this:

erl -noshell -s init stop -eval "io:format(\"hello\")."
Error! Failed to eval: io:format("hello").

This worked fine with 23,24 and 25.

Am I missing something?

Kind regards,


1 Like