Erlang OTP 27.0 Released

Erlang/OTP 27 Released

All artifacts for the release can be downloaded from the Erlang/OTP Github release and you can view the new documentation at

You can also install the latest release using kerl like this:

kerl build 27.0 27.0

The new Erlang/OTP 27 release contains new features, improvements as well as a few incompatibilities. Some of the new features are highlighted below.

Many thanks to all contributors!


EEP-59 has been implemented. Documentation attributes in source files can now be used to document functions, types, callbacks, and modules.

The entire Erlang/OTP documentation is now using the new documentation system.

New language features

  • Triple-Quoted Strings has been implemented as per EEP 64 to allow a string to encompass a complete paragraph.

  • Adjacent string literals without intervening white space is now a syntax error, to avoid possible confusion with triple-quoted strings.

  • Sigils on string literals (both ordinary and triple-quoted) have been implemented as per EEP 66. For example, ~"Björn" or ~b"Björn" are now equivalent to <<"Björn"/utf8>>.

Compiler and JIT improvements

  • The compiler will now merge consecutive updates of the same record.

  • Safe destructive update of tuples has been implemented in the compiler and runtime system. This allows the VM to update tuples in-place when it is safe to do so, thus improving performance by doing less copying but also by producing less garbage.

  • The maybe expression is now enabled by default, eliminating the need for enabling the maybe_expr feature.

  • Native coverage support has been implemented in the JIT. It will automatically be used by the cover tool to reduce the execution overhead when running cover-compiled code. There are also new APIs to support native coverage without using the cover tool.

  • The compiler will now raise a warning when updating record/map literals to catch a common mistake. For example, the compiler will now emit a warning for #r{a=1}#r{b=2}.

  • The order in which the compiler looks up options has changed.

    When there is a conflict in the compiler options given in the -compile() attribute and options given to the compiler, the options given in the -compile() attribute overrides the option given to the compiler, which in turn overrides options given in the ERL_COMPILER_OPTIONS environment variable.


    If some_module.erl has the following attribute:


    and the compiler is invoked like so:

    % erlc +warn_missing_spec some_module.erl

    no warnings will be issued for functions that do not have any specs.


  • The erl command now supports the -S flag, which is similar to the -run flag, but with some of the rough edges filed off.

  • By default, escripts will now be compiled instead of interpreted. That means that the compiler application must be installed.

  • The existing experimental support for archive files will be changed in a future release. The support for having an archive in an escript will remain, but the support for using archives in a release will either become more limited or completely removed.

    As of Erlang/OTP 27, the function code:lib_dir/2, the -code_path_choice flag, and using erl_prim_loader for reading members of an archive are deprecated.

    To remain compatible with future version of Erlang/OTP escript scripts that need to retrieve data files from its archive should use escript:extract/2 instead of erl_prim_loader and code:lib_dir/2.

  • The default process limit has been raised to 1048576 processes.

  • The erlang:system_monitor/2 functionality is now able to monitor long message queues in the system.

  • The obsolete and undocumented support for opening a port to an external resource by passing an atom (or a string) as first argument to open_port(), implemented by the vanilla driver, has been removed. This feature has been scheduled for removal in OTP 27 since the release of OTP 26.

  • The pid field has been removed from erlang:fun_info/1,2.

  • Multiple trace sessions are now supported.

  • configure now automatically enables support for year-2038-safe timestamps.

    By default configure scripts used when building OTP will now try to enable support for timestamps that will work after mid-January 2038. This has typically only been an issue on 32-bit platforms. If configure cannot figure out how to enable such timestamps, it will abort with an error message. If you want to build the system anyway, knowing that the system will not function properly after mid-January 2038, you can pass the --disable-year2038 option to configure, which will enable configure to continue without support for timestamps after mid-January 2038.’


  • There is a new module json for encoding and decoding JSON.

    Both encoding and decoding can be customized. Decoding can be done in a SAX-like fashion and handle multiple documents and streams of data.

    The new json module is used by the jer (JSON Encoding Rules) for ASN.1 for encoding and decoding JSON. Thus, there is no longer any need to supply an external JSON library.

  • Several new functions that accept funs have been added to module timer.

  • The functions is_equal/2, map/2, and filtermap/2 have been added to the modules sets, ordsets, and gb_sets.

  • There are new efficient ets traversal functions with guaranteed atomicity. For example, ets:next/2 followed by ets:lookup/2 can now be replaced with ets:next_lookup/1.

  • The new function ets:update_element/4 is similar to ets:update_element/3, but takes a default tuple as the fourth argument, which will be inserted if no previous record with that key exists.

  • binary:replace/3,4 now supports using a fun for supplying the replacement binary.

  • The new function proc_lib:set_label/1 can be used to add a descriptive term to any process that does not have a registered name. The name will
    be shown by tools such as c:i/0 and observer, and it will be included in crash reports produced by processes using gen_server, gen_statem, gen_event, and gen_fsm.

  • Added functions to retrieve the next higher or lower key/element from gb_trees and gb_sets, as well as returning iterators that start at given keys/elements.


  • Calls to ct:capture_start/0 and ct:capture_stop/0 are now synchronous to ensure that all output is captured.

  • The default CSS will now include a basic dark mode handling if it is preferred by the browser.


  • The functions crypto_dyn_iv_init/3 and crypto_dyn_iv_update/3 that were marked as deprecated in Erlang/OTP 25 have been removed.


  • The --gui option for Dialyzer has been removed.


  • The ssl client can negotiate and handle certificate status request (OCSP stapling support on the client side).


  • There is a new tool tprof, which combines the functionality of eprof and cprof under one interface. It also adds heap profiling.


  • As an alternative to xmerl_xml, a new export module xmerl_xml_indent that provides out-of-the box indented output has been added.

For more details about new features and potential incompatibilities see the README.


We had some snags with updating the documentation for Erlang/OTP 27. We hope to fix it soon. Meanwhile, the documentation can be found here: Erlang/OTP 27.0 — Erlang/OTP v27.0


Hi @MononcQc

After installing Erlang 27.0, my build started complaining about deprecated call to code:lib_dir/2 used by port-compiler (pc) module.

I then switched branch otp-27 of port-compiler but the issue persists.

$ erl
Erlang/OTP 27 [erts-15.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]
Eshell V15.0 (press Ctrl+G to abort, type help(). for help)

$ rebar3 version
rebar 3.22.0 on Erlang/OTP 26 Erts 14.2.1

$ rebar3 compile
===> Analyzing applications...
===> Compiling pc
===> Compiling ../../../opt/erlang/27.0/.cache/rebar3/plugins/pc/src/pc_port_env.erl failed
../../../opt/erlang/27.0/.cache/rebar3/plugins/pc/src/pc_port_env.erl:190:10: code:lib_dir/2 is deprecated; this functionality will be removed in a future release

I tried to deteled the rebar3 cache inside /opt/erlang/27.0/.cache but nothing changed.

I was able to reproduce this issue under macOS (Ventura) and Ubuntu 22.04 LTS.

Any idea?

The port compiler is a third party plug-in. I have always made a point not to touch it with a 10ft pole because I absolutely never wanted to have anything to do with the ownership of C compilation in the ecosystem if I could avoid it.

There’s an issue at Update port_compiler to support OTP 27.0-rc2 by avoiding code:lib_dir/2 · Issue #79 · blt/port_compiler · GitHub discussing this and pointing to remove use of lib_dir/2 bc it is deprecated in otp-27 by tsloughter · Pull Request #80 · blt/port_compiler · GitHub

I think @tsloughter just forgot to merge and release.

1 Like

@MononcQc thanks. But how it’s possible that rebar3 still try to compile the old pc even if I’m using the one from branch otp-27 ?

Even if I manually delete /opt/erlang/27.0/.cache/rebar3/plugins, it is recreated with the old pc after rebar3 compile:

$ rm -rf /opt/erlang/27.0/.cache
$ rebar3 compile
===> Analyzing applications...
===> Compiling pc
===> Compiling ../../../opt/erlang/27.0/.cache/rebar3/plugins/pc/src/pc_port_env.erl failed
../../../opt/erlang/27.0/.cache/rebar3/plugins/pc/src/pc_port_env.erl:190:10: code:lib_dir/2 is deprecated; this functionality will be removed in a future release

@tsloughter any chance to merge it to master?

OK found it. had this:

$ cat ~/.config/rebar3/rebar.config                                                                                                                                                                            {plugins, [rebar3_auto]}

I removed rebar3_auto and all good.

I’ve merged to master and released 1.15.0.

Thank you for another fantastic release! Two quick questions so we can plan accordingly:

  1. Is the plan to deprecate erlang:trace and related functions in the future or is the goal to always keep them around as a “singleton tracer”?

  2. Similarly, for ETS, is there a benefit in keeping the old ets:next, ets:first, etc now that we have the lookup variants?

So, do I understand correctly that we now need ExDoc even to generate EEP48 chunks?

ExDoc reads the chunks to generate the documentation. I believe today the chunks are generated by the compiler based on -doc attributes and similar.

The only reason to keep erlang:trace and friends would be for backward compatibility. I think a deprecation would be good to signal that there is something better to use.

I think there are actual use cases for the old ets:next where you don’t want to waste time and space to (always) lookup the entire tuple.



The reason I’m asking is that while working on the OpenBSD port I noticed that make docs with DOC_TARGETS=chunks in the environment does not seem to generate eep48 chunks in OTP27, whereas it did with OTP25 and OTP26 …

The EEP48 doc chunks are for most application embedded in the .beam file in 27. edoc still uses the old convention of placing them in the doc/chunks/ folder (and needs to be built using make docs DOC_TARGETS=chunks)

There is no way to create the .chunk files like pre 26 builds used to do.

1 Like

But why? Why ex_doc’s html instead of native edoc?

Erlang/OTP 27 Highlights - Erlang/OTP has the technical answers to “why not edoc?”

  1. xml
  2. the xml was separate from the source code
  3. the xml was unenjoyable and high-effort and tedious to edit

and some answers to “why exdoc?”:

  1. a goal was “a tool more widely used so that we wouldn’t have to carry the entire burden for maintaining it.”
  2. exdoc is such a tool, and is very popular with Elixir and Elixir projects

Dogfooding’s good, and Elixir’s close enough. Actually, a couple of weeks ago I wrote this in my notes:

Tragically, Erlang uses fop for documentation, which is a massive pain to build as it uses Java’s alien toolchains. If your build system can supply fop, use that. Otherwise, you can unpack a binary distribution …

… after completely failing to get fop to build, after configure complained that it was missing. fop is still mentioned in the Github dockerfile, but was removed from HOWTO/, so I think that’s gone. Java’s more foreign than Elixir, right?

Back to exdoc: I didn’t see any comments about the beauty of it vs. edoc. What specifically is so ugly? I’ve already liked some things about the new docs:

  1. the separate ‘modules’ tab on the sidebar
  2. the version selections when searching
  3. it’s much more readable in w3m (a terminal web browser). The old docs rendered the sidebar above the main content.

The text is still excellent, and the search is still a bit frustrating. The stylistic differences seem pretty minor to me.

On frustrating search: I go to Getting Started and want to learn about maps. Should I use the search box at the top? No, that takes me to hits from the ‘maps’ module. Should I ctrl-f? No, the only hit on that page is to the maps section of the efficiency guide - which is great, but still not “getting started” material. To find the right section I need to expand the ‘Sequential Programming’ section on the sidebar. The Erlang docs still require some familiarity with the layout, to use effectively.


Side by side:

The old docs were much more terse about “f/1 expands to f/2” procs, and I really liked the consolidated Types section.

The new docs have the exact -spec directive, </> source code links on the right, tooltips when you hover over a type link, and more syntax highlighting.

Also: old docs have prominent PDF links. New docs have ePUB links at the bottom of the page. The ePUBs have working hyperlinks, much nicer search (in Foliate), and probably render better in an e-reader. The PDFs probably print better, and your browser is more likely to have out-of-the-box support for them.


This was one issue that I have had reading the new docs, the collapsed function heads were much more convenient. I think this might be less of an issue in Elixir-land due to the proplist (Keyword list) arguments.

This is something we would be willing to incorporate into ExDoc. Perhaps we could add a -doc #{collapse := {foo, 3}} metadata or similar and ExDoc would merge them somehow.

In Elixir it occurs less because of default arguments but it might still occur in other situations.


Thanks - then the port is good to go :slight_smile:

1 Like