Skip to content

[eas-cli] Show why a simulator session failed to start - #4693

Merged
brentvatne merged 4 commits into
mainfrom
@brent/simulator-start-error-detail
Oct 20, 2026
Merged

brentvatne merged 4 commits into
mainfrom
@brent/simulator-start-error-detail

Conversation

@brentvatne

@brentvatne brentvatne commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Why

When a simulator session fails, eas simulator:start prints only the session status and a link:

✖ Failed while polling for agent-device session to be ready
Simulator session 01a15697-… errored before the agent-device session was ready. https://expo.dev/…
    Error: simulator command failed.

The reason is already on the session's job run. In this case turtleJobRun.errors held EAS_IOS_SIMULATOR_NOT_FOUND with No available iOS Simulator is named "iPhone 16 Pro"…. To find it, you had to open the session page and expand the failed step.

How

  • New DeviceRunSessionQuery.jobRunErrorsByIdAsync fetches the job run's errors { errorCode message docsUrl }. It runs only once a session has failed, so the shared byIdAsync query, which runs on every poll and backs simulator:get and simulator:control, is unchanged. If it fails, the CLI prints the old message.
  • The lookup can't hold up stopping the session:
    • it has a 10s deadline;
    • it stops on Ctrl+C, and is skipped if Ctrl+C already happened;
    • aborting it unsubscribes from urql, which cancels the request;
    • it isn't retried, and it doesn't print the "transient error, try again" message, since it's best-effort.
  • Each message is capped at 500 characters.
  • Each job run error is printed on its own line, with a "Learn more" link when it has a docsUrl.
  • This applies to sessions that error, stop or finish before they are ready, and to a session that errors after it is ready. That last case used to print only Simulator session X errored.
  • generated.ts only adds the new query's types, matching codegen output exactly.

After, for an unknown device on iOS:

✖ Failed while polling for agent-device session to be ready
Simulator session 01a1569f-… errored before the agent-device session was ready. https://expo.dev/…
No available iOS Simulator is named "iPhone 16 Pro". Run `xcrun simctl list devices available` on the device host to see the devices it offers.

For failures without a specific error code, this PR prints the job run's own message (for example "Unknown error. See logs for more information."). Reading the failed step's cause from the job run logs is in #4694, which bounds the download and prints only the masked log message.

Test Plan

  • npx jest src/commands/simulator src/graphql/queries/__tests__/DeviceRunSessionQuery-test.ts: 204 tests pass. The new tests assert the exact error message for:
    • no job run errors;
    • multiple errors with a docs link;
    • an unknown error;
    • a failed errors query;
    • a lookup that never settles, which still lets the session be stopped;
    • a long message, which is truncated;
    • an error after the session is ready.
  • DeviceRunSessionQuery-test.ts runs a real urql client against a loopback server and checks that:
    • a transient error prints nothing;
    • network errors aren't retried;
    • aborting closes the connection;
    • an already-aborted signal sends no request.
  • yarn typecheck, oxlint and oxfmt --check pass.
  • Ran simulator:start --device "iPhone 16 Pro" with this branch against production. The output above is from that run.

🤖 Generated with Claude Code

simulator:start only printed the session status when a session errored
before it was ready. The reason is already on the session's job run, so
fetch turtleJobRun.errors and print each message (and docs link) on its
own line.
@github-actions

Copy link
Copy Markdown

Subscribed to pull request

File Patterns Mentions
packages/eas-cli/** @douglowder

Generated by CodeMention

Warning: The preamble and epilogue options in commentConfiguration are deprecated. Use template instead.

@codecov

codecov Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.11111% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.30%. Comparing base (aae7d13) to head (a4f6ea8).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...s-cli/src/graphql/queries/DeviceRunSessionQuery.ts 89.29% 3 Missing ⚠️
packages/eas-cli/src/commands/simulator/index.ts 94.12% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4693      +/-   ##
==========================================
+ Coverage   75.23%   75.30%   +0.08%     
==========================================
  Files        1053     1053              
  Lines       52000    52191     +191     
  Branches    11345    11397      +52     
==========================================
+ Hits        39118    39299     +181     
- Misses      11894    11904      +10     
  Partials      988      988              

☔ 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.

…s for unknown errors

Fetch job run errors with a separate query when a session fails instead of
on every poll of the shared session query. When the job run only reports an
unknown error, print the failed step errors from its logs. Also show the
errors when a session fails after it is ready.
@brentvatne
brentvatne requested a review from gwdp October 20, 2026 00:20

async function fetchJobRunLogErrorsAsync(logFileUrl: string): Promise<string[]> {
try {
const response = await fetch(logFileUrl);

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.

Non-blocking: explicit timeout

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This was on the log download, which is no longer in this PR. The errors query that replaced it now has an explicit 10s deadline (JOB_RUN_ERRORS_TIMEOUT_MS, combined with the Ctrl+C signal). The log download moved to #4694, where it has its own 10s timeout and a 20 MiB size limit.

Drop the job run log fallback from this change, which printed unmasked
err.message lines, read logs for stopped sessions and had no timeout or size
limit. The bounded, masked log fallback lands in the simulator:get follow-up.
Give the lookup a 10s deadline and the session's Ctrl+C signal, and cancel
its request by unsubscribing from urql, so it can't hold up stopping the
session. Don't retry it or print the transient-error message, since it's
best-effort. Cap each error message at 500 characters.
@github-actions

Copy link
Copy Markdown

✅ Thank you for adding a changeset!

@brentvatne
brentvatne merged commit f775b10 into main Oct 20, 2026
10 checks passed
@brentvatne
brentvatne deleted the @brent/simulator-start-error-detail branch October 20, 2026 20:51
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.

2 participants