Skip to content

Handle the cli daemon startup failures more gracefully with better errors - #6241

Merged
canova merged 4 commits into
firefox-devtools:mainfrom
canova:cli-daemon-errors
Aug 6, 2026
Merged

Handle the cli daemon startup failures more gracefully with better errors#6241
canova merged 4 commits into
firefox-devtools:mainfrom
canova:cli-daemon-errors

Conversation

@canova

@canova canova commented Aug 5, 2026

Copy link
Copy Markdown
Member

Main | Deploy preview

Fixes #6230.

The scope of this PR was smaller but I went into this rabbit hole of improving this code and it turned out a lot bigger than I initially intended. But I think this improves the whole daemon startup path drastically. See the individual commits for more info. Now, the cli returns error messages like this:

When the session directory is not writable:

Error: Cannot write to the profiler-cli session directory ~/.profiler-cli.
Permission denied. Sandboxes (agent sandboxes, containers, restricted CI runners) commonly deny access outside the workspace, including the home directory.
Underlying error: EACCES: permission denied, open '~/.profiler-cli/.write-probe-28702'
This is the default session directory (PROFILER_CLI_SESSION_DIR is not set).
Point profiler-cli somewhere writable, for example:
  PROFILER_CLI_SESSION_DIR=/var/folders/.../T/profiler-cli profiler-cli load <PATH>
A directory inside your workspace also works, as long as the socket path stays short.

When the unix sockets are not allowed:

Error: The profiler-cli daemon exited during startup (exit code 3):
    Not allowed to create the Unix domain socket at ~/.profiler-cli/ewzneg2rzye.sock.
    profiler-cli needs a Unix domain socket to talk to its daemon. If you are inside a sandbox, allow Unix domain sockets in the sandbox policy, point PROFILER_CLI_SESSION_DIR at a directory the sandbox can write to, or run profiler-cli outside the sandbox.
    Underlying error: listen EPERM: operation not permitted ~/.profiler-cli/ewzneg2rzye.sock

And there are a few more other combinations in case socket path is too long, the socket path is already occupied, a session can't be reached, when the connect() is denied, etc.

@canova
canova requested review from fatadel and mstange August 5, 2026 10:10
@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.69%. Comparing base (0fc5218) to head (a62903b).
⚠️ Report is 9 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6241   +/-   ##
=======================================
  Coverage   83.69%   83.69%           
=======================================
  Files         348      348           
  Lines       37403    37433   +30     
  Branches    10498    10514   +16     
=======================================
+ Hits        31303    31330   +27     
- Misses       5673     5676    +3     
  Partials      427      427           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@canova
canova force-pushed the cli-daemon-errors branch from ed82391 to 4f46b30 Compare August 5, 2026 10:36

@mstange mstange left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

Check the socket path length there too. The kernel rejects an over-long
sockaddr_un with a bare EINVAL that names neither the limit nor the
path.

Thanks, operating systems.

canova added 4 commits August 6, 2026 12:55
We spawn the daemon detached with its stdio discarded, so a fatal
startup error was just an exit code. A denied listen() even exited 0,
since the server error handler routed through shutdown().

This commit adds a reportFatalError() that writes the reason to a
<sessionId>.error file, appends it to the log, and exits 3, plus a
diagnostics.ts that turns the errnos behind these failures into
explanations. The following commits are going to read this file.

Also it fixes the log stream being created before the session directory
exists.
Previously we only output "Daemon process exited unexpectedly during
startup" on daemon startup failure. Now we report the reason the daemon
records, which is the startup error file first, then the tail of its
log.

When neither exists, report what is known instead of guessing. If the
daemon exited, print the command to rerun the spawn in the foreground.
If it is still running and simply has not published its metadata, say
that and name the pid, rather than declaring it dead and blaming the
sandbox.
An unusable session directory is the one failure the daemon cannot
report, since the error file it would report through lives in that
directory. Check it in the client instead: stat, mkdir, and a write
probe, because mkdirSync() succeeds on an existing directory that cannot
be written to.

Check the socket path length there too. The kernel rejects an over-long
sockaddr_un with a bare EINVAL that names neither the limit nor the
path.

Also stop "session list" from creating the session directory just to find no
sessions in it.
A dead daemon and a sandbox that forbids connect() look the same to
validateSession(), and both ended in cleanupSession(). Deleting the socket file
of a live daemon makes it unreachable for good, and deleting the metadata hides
it from "session list" and "stop --all", leaving a process holding a profile that
no command can find or stop.

This patch gates cleanup on the errnos that prove nothing is listening:
ENOENT, ECONNREFUSED, and ENOTSOCK. On EACCES, EPERM or ETIMEDOUT, keep
the files and report the session as unreachable with its pid.
@canova
canova force-pushed the cli-daemon-errors branch from 4f46b30 to a62903b Compare August 6, 2026 10:55
@canova
canova enabled auto-merge August 6, 2026 10:56
@canova
canova merged commit 447ab23 into firefox-devtools:main Aug 6, 2026
21 checks passed
@canova canova mentioned this pull request Aug 10, 2026
canova added a commit that referenced this pull request Aug 10, 2026
Changes:

[fatadel] Create the Network track from the timeline-network schema
display location (#6224)
[Markus Stange] Only call `getRawFrameTableBuilderWithExistingContents`
once per symbolication batch. (#6233)
[fatadel] Improve discoverability of downloading a local profile (#6216)
[Nazım Can Altınova] Handle the cli daemon startup failures more
gracefully with better errors (#6241)
[Nazım Can Altınova] Add the ability to apply source maps from the CLI
(#6229)
[Nazım Can Altınova] Handle Text and Log marker payloads with their
marker schema (#6247)
[Nazım Can Altınova] Bump the Gecko profile version to make sure that
the Text and Log marker changes are picked up in the frontends (#6252)
[fatadel] Deactivate a menu button as soon as its panel is dismissed
(#6251)
[Nazım Can Altınova] 🔃 Sync: l10n -> main (August 10, 2026) (#6253)
[Nazım Can Altınova] Bump profiler-cli version to 0.8.0 (#6254)

And special thanks to our localizers:

de: Ger
de: Michael Köhler
el: George kitsoukakis
en-CA: chutten
en-CA: Saurabh
en-GB: Ian Neal
es-CL: ravmn
fy-NL, nl: Fjoerfoks
fr: Théo Chevalier
fy-NL: Fjoerfoks
ia: Melo46
it: Francesco Lodolo [:flod]
nl: Fjoerfoks
ru: michellemelsspam
ru: Valery Ledovskoy
tr: giray
tr: Selim Şumlu
zh-TW: Pin-guang Chen
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Handle the failure cases of the profiler-cli daemon creation and communication gracefully

2 participants