Skip to content

fix(core): restore sessions and propagate errors on session/within/retryTo error paths#5633

Merged
DavertMik merged 1 commit into
advisor/001-promise-core-characterizationfrom
advisor/006-promise-core-fixes
Jun 13, 2026
Merged

fix(core): restore sessions and propagate errors on session/within/retryTo error paths#5633
DavertMik merged 1 commit into
advisor/001-promise-core-characterizationfrom
advisor/006-promise-core-fixes

Conversation

@DavertMik

@DavertMik DavertMik commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

What

Implements the actual fixes for the latent promise-core divergences that #5622 characterized. Each fix makes an error path symmetric with its already-working success path, and the corresponding characterization assertion is updated to the corrected behavior in the same commit.

⚠️ Stacked on #5622 (base = advisor/001-promise-core-characterization); review only the fix(core): … commit. Merge #5622 first.

Fixes (4 of the 7 findings)

# Finding Fix
5 within() async error skipped _withinEnd and detached the error catch now runs finishHelpers() (so _withinEnd runs on error) and return recorder.promise() (so the error propagates through within())
3 session() async error leaked the session id catch now schedules recorder.session.restore via recorder.add so the recorder session is restored on error
4 session() sync queued-throw leaked the session id the finally recorder.catch now calls recorder.session.restore before re-throwing (the only place that runs on a rejected chain)
6 retryTo() rejected prematurely / tries === 2 on first a thrown callback routes through recorder.throw instead of an immediate reject(err), so the retry logic owns the outcome — throw-then-succeed now resolves; reject only after maxTries. tries starts at 1; retry count preserved (tries < maxTries)

Important: the session fix is deliberately surgical

The session error paths are entangled with the Playwright browser lifecycle. An earlier version of this PR routed the async-error cleanup through finalize() — which also swaps the original (no-op) restoreVars(sessionName) call for the real restoreVars(). That closed the browser context under BROWSER_RESTART=session and failed the Playwright CI job (4 Cannot create page: browser context has been closed failures in session_test.js).

The fix is now minimal: only add the missing recorder.session.restore (the session-id leak the finding is about); the existing restoreVars/listener cleanup is left exactly as-is. Net lib/session.js change is two added lines.

Verification

  • npm run test:unit748 passed, 0 failed, 11 skipped
  • npm run test:runner273 passed, 0 failed — incl. should rerun flaky tests, should rerun retried steps, should use retryFailedStep plugin for failed steps, etc.
  • Acceptance (real Chromium), both restart modes — BROWSER_RESTART=browser and BROWSER_RESTART=sessionwithin/session/els green, 0 browser context has been closed failures. (The one remaining local acceptance failure is config_test.js's API call to the local mock at :3001/api/users, which 404s only in this sandbox and passes in CI — unrelated to these changes.)

Deliberately not changed here

  1. recorder.retries never cleared by reset()lib/plugin/retryFailedStep.js (on by default) reads/mutates recorder.retries; the in-code comment warns clearing it breaks the plugin. Needs per-test scoping coordinated with that plugin.
  2. Stopped recorder → silent hangadd()'s no-op-when-stopped is a contract the should not add steps when stopped test depends on; these error-path fixes reduce the wedging, but a blanket change is unsafe.
  3. Nested cross-level restore order — low severity; real within/session keep start+restore at one level. The characterization test pins the correct synchronous id semantics.

Happy to tackle any of these three as a focused follow-up.

🤖 Generated with Claude Code

…tryTo error paths

Fixes four of the latent error-path divergences characterized in #5622, by
making the error paths symmetric with the success paths. The characterization
assertions are updated to the corrected behavior in the same commit.

- within() async error (lib/effects.js): the catch now calls finishHelpers()
  (so helpers' _withinEnd runs on error, not just success) and returns
  recorder.promise() (so the error propagates through within() instead of being
  detached onto a trailing task that a caller awaiting within() never sees).

- session() async error (lib/session.js): schedules a recorder task that runs
  recorder.session.restore so the recorder session is restored on error (it
  was only restored on success, leaking the session id). The existing
  restoreVars/listener cleanup is kept as-is — switching to finalize()'s real
  restoreVars() closes the browser context under BROWSER_RESTART=session.

- session() sync error (lib/session.js): the finally recorder.catch now calls
  recorder.session.restore before re-throwing (the only place that runs on a
  rejected chain).

- retryTo() (lib/effects.js): a thrown callback no longer reject()s the outer
  promise prematurely — it routes through recorder.throw so the retry logic
  owns the outcome (retry, or reject once maxTries is exhausted). A callback
  that throws then succeeds on a later attempt now resolves instead of
  rejecting. tries now starts at 1 on the first attempt (was 2); the retry
  count is preserved (tries < maxTries).

Verified: unit 748/0, runner 273/0 (incl. all retryFailedStep/rerun tests),
acceptance within/session/els green under both BROWSER_RESTART=browser and
=session.

Not changed here (would be unsafe or out of scope, see PR):
recorder.retries clearing (retryFailedStep depends on it), the stopped-recorder
no-op contract, and nested cross-level restore ordering.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@DavertMik DavertMik force-pushed the advisor/006-promise-core-fixes branch from f921147 to dca7626 Compare June 13, 2026 15:00
@DavertMik DavertMik merged commit 974e699 into advisor/001-promise-core-characterization Jun 13, 2026
11 checks passed
@DavertMik DavertMik deleted the advisor/006-promise-core-fixes branch June 13, 2026 15:30
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.

1 participant