Appium 4's first beta moves status text to stdout and drops three Node lines, including the one this Mac runs
appium@4.0.0-beta.0 landed on npm at 20:39:59 UTC on 19 September 2026. Two changes break a rig before any test runs, and the iOS driver has not shipped against it yet.
Appium 4.0.0-beta.0 went to npm at 20:39:59 UTC on 19 September 2026 and changed two things
that break a rig before a single test runs. Human-readable CLI output moved from STDERR to STDOUT,
so a script that captured STDERR to log progress now captures nothing and a script that threw STDOUT
away now throws the progress away instead. And the Node engine range tightened from
^20.19.0 || ^22.12.0 || >=24.0.0 in 3.7.0 to
^22.22.2 || ^24.15.0 || >=26.0.0. That range excludes the whole of Node 20, 23 and 25.
The Mac this note was written on runs Node v25.8.2, which 3.7.0 accepted and the beta does not.
What shipped, and when
The 4.0.0-beta.0 tag was published on GitHub at 20:35:49 UTC on 19 September 2026 and reached the
npm registry four minutes later. A patch, 4.0.0-beta.1, followed 49 minutes after that with a single
fix: catch peerDependencies up to the published beta.0. The npm beta dist-tag now points
at 4.0.0-beta.1; latest is still 3.7.0, published 24 August 2026.
| Field | appium 3.7.0 | appium 4.0.0-beta.0 | Source |
|---|---|---|---|
| npm publish time | 2026-08-24T20:52:07Z | 2026-09-19T20:39:59Z | registry.npmjs.org/appium |
engines.node | ^20.19.0 || ^22.12.0 || >=24.0.0 | ^22.22.2 || ^24.15.0 || >=26.0.0 | registry.npmjs.org/appium |
engines.npm | >=10 | >=10 | registry.npmjs.org/appium |
package type | not set (CommonJS) | module | registry.npmjs.org/appium |
| Default log level | debug | info | 4.0.0-beta.0 release notes |
| Status text stream | STDERR | STDOUT | 4.0.0-beta.0 release notes |
MJSONWP GET /session/:id | present | removed | 4.0.0-beta.0 release notes |
--allow-cors | present | removed, use --allow-insecure=*:cors | 4.0.0-beta.0 release notes |
| appium-xcuitest-driver support | shipped | PR #2984, opened 2026-09-20, open | github.com/appium/appium-xcuitest-driver/pull/2984 |
Where the status text goes now
The release notes state the change three times in three different framings, which is a fair signal
of how much downstream code it touches. Before, non-JSON human-readable output (info, success and
warning messages from driver list, plugin install, doctor and
friends) was written to STDERR, and STDOUT was reserved almost exclusively for --json.
Now that output goes to STDOUT and only genuine errors go to STDERR. --json is
unaffected: it still writes clean JSON to STDOUT alone.
The same release moved @appium/logger's Log class default stream from
STDERR to STDOUT for every level except error, and added an errorStream and
stderrLevel pair. Code that set .stream on a Log instance
expecting it to capture all levels has to set .errorStream to the same target now.
Which Node versions the beta will actually start on
Three disjoint windows, and the gaps between them are not decoration. ^22.22.2 means
at least 22.22.2 and below 23. ^24.15.0 means at least 24.15.0 and below 25.
>=26.0.0 takes everything from 26 up. Node 20, which 3.7.0 accepted from 20.19.0,
is gone entirely, and so are the odd-numbered current lines 23 and 25.
Those eight version results are not read off the range by eye. They come from running the
semver CLI against the published range, which is the same resolver npm uses, and the
command is in the section below.
The ESM change is the one that will surface late
3.7.0's package manifest has no type field, so it is CommonJS.
4.0.0-beta.0 sets "type": "module". Along with it the release removes default exports
from @appium/base-driver, @appium/base-plugin and
@appium/logger, so require('appium') and
const BasePlugin = require('@appium/base-plugin') both stop working. A team that
consumes Appium only through the CLI will not see this. A team that wrote a plugin will, and will see
it at import time rather than at test time.
Two more removals matter for anything that talks to the server directly rather than through a
client library. BaseDriver.getSession and the JSONWP
GET /session/:sessionId route are gone; the replacements are
getAppiumSessionCapabilities for capabilities and
EventCommands.getLogEvents for event history. The
appium:eventTimings capability went with them, because it only ever controlled whether
getSession's response carried event history that getLogEvents already
returns unconditionally. Separately, SPDY and HTTP/2 negotiation are gone, leaving plain HTTP/1.1.
What this costs a team driving real iPhones
Nothing yet, and that is the point. appium-xcuitest-driver has not shipped against Appium 4:
PR #2984, "feat!: target Appium 4 beta", was opened on 20 September 2026 and is still open as of
22 September. Its own description says it bumps the appium peer dependency from
^3.0.0-rc.2 to ^4.0.0-beta.0 and updates driver code for the API removals.
So an iOS rig cannot move to Appium 4 today even if it wants to, and that gap is the window to fix
the plumbing.
The ordering judgement, ours: fix the stream capture first, because it fails silently. A Node version mismatch stops the process with a message. A status line that has moved to the other stream produces a green build with an empty log, which is the failure mode that costs a day of debugging three weeks later. If you have a CI step that greps STDERR for "Driver installed" or similar, it will simply never match.
Note also what did not change. --json still writes clean JSON to STDOUT only, and
errors still go to STDERR. A script that already used --json and parsed STDOUT is
unaffected by the whole stream rearrangement. That is an argument for using the machine-readable flag
in a rig even when a human is watching.
Adjacent, and worth knowing if you pin WebDriverAgent separately: WDA v16.12.9 shipped on
19 September 2026 with one fix, "resolve real device udid to xcodebuild's own case before building
-destination id=" (issue #1260, opened 18 September). That is the same case-sensitivity
class of bug we measured across five tools last week, now fixed upstream at the point where the UDID
reaches xcodebuild.
Check it yourself
Every number above comes from one of these three commands. None of them need a device.
# engine ranges, publish times and the ESM flag, straight from the registry
curl -s https://registry.npmjs.org/appium | python3 -c "
import json,sys
d=json.load(sys.stdin)
print('dist-tags:', d['dist-tags'])
for v in ['3.7.0','4.0.0-beta.0','4.0.0-beta.1']:
m=d['versions'][v]
print(v, m.get('engines'), 'type=', m.get('type'), d['time'][v])
"
# which Node versions satisfy the beta's range
npx --yes semver@7 -r "^22.22.2 || ^24.15.0 || >=26.0.0" \
22.22.2 23.0.0 24.15.0 24.20.0 25.0.0 25.8.2 26.0.0
# prints only: 22.22.2 24.15.0 24.20.0 26.0.0
# the breaking-change list, verbatim
curl -s "https://api.github.com/repos/appium/appium/releases/tags/appium%404.0.0-beta.0" \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(d['published_at']); print(d['body'])"
To see the stream change on your own machine without upgrading anything, run any Appium CLI
subcommand twice under 3.7.0, once with 1>/dev/null and once with
2>/dev/null, and note which one still prints. Under 4.0.0-beta.0 the answer flips.
What would prove this wrong
Two claims here are dated predictions rather than measurements, and both are checkable before the end of 2026.
First: that appium-xcuitest-driver's first release supporting Appium 4 carries
engines.node identical to ^22.22.2 || ^24.15.0 || >=26.0.0. PR #2984 says
it bumps engines.node to match Appium 4's minimum, so a published driver with a wider
range would mean the constraint is negotiable and the Node 25 exclusion is not load-bearing.
Second: that the STDOUT move survives to 4.0.0 stable. It is listed as a bug fix, not a feature,
which is a mild signal that the maintainers consider the old layout the defect. If a later beta
reverts it or adds a compatibility flag, the migration advice above is wasted work. Check
appium@4.0.0's release notes for a --legacy-streams style escape hatch when
it lands.
Sources
- Appium, release notes for appium@4.0.0-beta.0, published 19 September 2026
- npm registry metadata for the appium package: engines, type and publish times, read 22 September 2026
- Appium, release notes for appium@4.0.0-beta.1, published 19 September 2026
- appium-xcuitest-driver PR #2984, 'feat!: target Appium 4 beta', opened 20 September 2026, open as of 22 September 2026
- WebDriverAgent v16.12.9, published 19 September 2026, resolving real device UDID case for -destination id=
- WebDriverAgent issue #1260, opened 18 September 2026