Skip to content

1.6.0: Elixir's CLI consumes application arguments and halts the VM #229

Description

@joshrotenberg

Two regressions in 1.6.0, same root cause. Verified with a 12-line application whose only dependency is burrito.

Context

1.5.x can no longer build natively on macOS. macos-latest is macos-26-arm64, and Zig 0.15.2 cannot link against the macOS 26 SDK:

error: undefined symbol: __availability_version_check
error: undefined symbol: _dispatch_queue_create
error: undefined symbol: _arc4random_buf

1.6.0 moves to Zig 0.16 and builds there. So neither released version both builds on current macOS and runs correctly.

1. Application arguments are consumed

$ probe hello world
No file named hello

$ probe --version
Erlang/OTP 29 [erts-17.0.3] ...
Elixir 1.20.2 (compiled with Erlang/OTP 29)

$ probe --help
--help : Standalone options can't be combined with other options

The application receives its arguments correctly; printing from start/2 shows :init.get_plain_arguments/0 returning ["hello", "world"]. They are consumed afterwards.

2. The VM halts, killing long-running applications

Independent of arguments. This application never halts:

def start(_type, _args) do
  spawn(fn -> Process.sleep(60_000) end)
  Supervisor.start_link([], strategy: :one_for_one, name: ArgvProbe.Sup)
end

Run with no arguments at all:

result
1.6.0 exits after 1289ms, rc=0
1.5.0 semantics stays up

Servers, Phoenix applications, and anything else expecting to keep running exit immediately after boot.

Cause

src/erlang_launcher.zig boots the release with -s elixir start_cli and appends the application's arguments after -extra.

In 1.5.0 that flag was a single argv element, "-s elixir start_cli". erl does not split argv elements, so it was never recognised and start_cli never ran. 1.6.0 splits it into "-s", "elixir", "start_cli" (lines 59-61), so it now runs.

Elixir's CLI then does what it is supposed to do: claims --version and --help, treats the first remaining plain argument as a script path, and halts the VM when it finishes.

$ elixir nosuchthing
No file named nosuchthing

$ elixir -- foo bar        # exits in 149ms
$ elixir --no-halt -- foo bar    # stays up

Fix

Emit --no-halt and -- after -extra, and strip them back off in Burrito.Util.Args. --no-halt restores the 1.5.0 lifetime, -- stops the CLI claiming arguments, and start_cli still runs, so whatever #225 fixed by splitting the flag is unaffected.

Reverting to the single joined element would also fix both, but only by making start_cli silently not run again.

Verified on macOS 26 arm64 and Linux arm64: arguments arrive intact including --version and --help, long-running applications stay up, exit codes propagate, maintenance subcommands work, and the EPIPE handling from #225 still terminates cleanly when piped into head.

Note System.argv/0 is populated only once start_cli runs, which is after Application.start/2, so applications still need Burrito.Util.Args.

PR follows.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions