Skip to content

Handle fire-and-forget RSCRoute retry failures - #4378

Merged
justin808 merged 1 commit into
mainfrom
codex/batch-e-pro-route
Jul 2, 2026
Merged

Handle fire-and-forget RSCRoute retry failures#4378
justin808 merged 1 commit into
mainfrom
codex/batch-e-pro-route

Conversation

@justin808

Copy link
Copy Markdown
Member

Summary

Fixes #4330.

Production RSCRoute recover-on-error retries preserve the previous route content, but a fire-and-forget caller could still leave the returned retry promise unhandled after the route recorded the recoverable error. This keeps the caller-visible promise rejected while attaching an internal no-op rejection handler in production recover-on-error mode.

Changes

  • Preserve the existing refetch/retry rejected promise contract for callers that await or catch it.
  • Add a production recover-on-error handler so fire-and-forget retry failures do not surface as unhandled rejections.
  • Add a regression test that retries from inline controls without catching the retry promise and asserts no unhandledRejection is emitted.

Validation

  • pnpm --filter react-on-rails-pro exec jest tests/imperativeRefetch.client.test.tsx --runInBand
  • pnpm --filter react-on-rails-pro run type-check
  • pnpm exec prettier --check packages/react-on-rails-pro/src/RSCRoute.tsx packages/react-on-rails-pro/tests/imperativeRefetch.client.test.tsx
  • pnpm exec eslint packages/react-on-rails-pro/src/RSCRoute.tsx packages/react-on-rails-pro/tests/imperativeRefetch.client.test.tsx --no-warn-ignored
  • script/check-pro-license-headers
  • git diff --check origin/main...HEAD
  • codex review --base origin/main

No changelog entry: internal Pro client robustness fix with no public API change.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@justin808, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 10 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f09e8fbc-9039-4c41-9240-4b23f4ea1d82

📥 Commits

Reviewing files that changed from the base of the PR and between e03a4ad and a17d813.

📒 Files selected for processing (2)
  • packages/react-on-rails-pro/src/RSCRoute.tsx
  • packages/react-on-rails-pro/tests/imperativeRefetch.client.test.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/batch-e-pro-route

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@justin808

Copy link
Copy Markdown
Member Author

+ci-status

@justin808

Copy link
Copy Markdown
Member Author

+ci-run-hosted

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

CI Status

Head SHA: 43b57358d92b
Changed files: 2
Docs-only heuristic (matches ci-changes-detector metadata paths): no
ready-for-hosted-ci label: absent
force-full-hosted-ci label: absent
Current hosted-CI waiver: not present for this SHA

Only the required gate is active unless hosted CI is requested.

@github-actions github-actions Bot added the ready-for-hosted-ci Run optimized hosted GitHub CI for this PR label Jul 2, 2026
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Hosted CI Requested

Triggered 9 workflow(s) for 43b57358d92b.
Mode: optimized hosted CI (path-selected by script/ci-changes-detector).
Added ready-for-hosted-ci, so future commits will keep running optimized hosted CI until +ci-stop-hosted is used.

View progress in the Actions tab.

@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR updates production retry handling for RSCRoute. The main changes are:

  • Adds an internal no-op rejection handler for recover-on-error refetches.
  • Keeps the returned refetch and retry promise rejected for callers that await it.
  • Adds a regression test for inline fire-and-forget retry failures.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.
  • The returned promise still rejects because the swallowed continuation is separate from the promise returned to callers.
  • The new test cleans up its rejection listener, and the existing test lifecycle restores NODE_ENV.

Important Files Changed

Filename Overview
packages/react-on-rails-pro/src/RSCRoute.tsx Adds production-only suppression for unhandled fire-and-forget retry rejections while preserving caller-visible rejection behavior.
packages/react-on-rails-pro/tests/imperativeRefetch.client.test.tsx Adds inline retry controls and a regression test for production fire-and-forget retry failures.

Reviews (1): Last reviewed commit: "Avoid unhandled RSCRoute retry rejection..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size
react-on-rails/client bundled (gzip) 63.51 KB (0%)
react-on-rails/client bundled (gzip) (time) 63.51 KB (0%)
react-on-rails/client bundled (brotli) 54.51 KB (0%)
react-on-rails/client bundled (brotli) (time) 54.51 KB (0%)
react-on-rails-pro/client bundled (gzip) 64.54 KB (0%)
react-on-rails-pro/client bundled (gzip) (time) 64.54 KB (0%)
react-on-rails-pro/client bundled (brotli) 55.53 KB (0%)
react-on-rails-pro/client bundled (brotli) (time) 55.44 KB (-0.17% 🔽)
registerServerComponent/client bundled (gzip) 75.91 KB (+0.03% 🔺)
registerServerComponent/client bundled (gzip) (time) 75.91 KB (+0.03% 🔺)
registerServerComponent/client bundled (brotli) 65.4 KB (-0.03% 🔽)
registerServerComponent/client bundled (brotli) (time) 65.4 KB (-0.03% 🔽)
wrapServerComponentRenderer/client bundled (gzip) 68.37 KB (0%)
wrapServerComponentRenderer/client bundled (gzip) (time) 68.37 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) 58.76 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) (time) 58.76 KB (0%)

@justin808
justin808 force-pushed the codex/batch-e-pro-route branch from 43b5735 to a17d813 Compare July 2, 2026 07:33
@justin808

Copy link
Copy Markdown
Member Author

Rebased onto origin/main b194bb67f after main advanced.

Post-rebase validation:

  • pnpm --filter react-on-rails-pro exec jest tests/imperativeRefetch.client.test.tsx --runInBand
  • pnpm --filter react-on-rails-pro run type-check
  • git diff --check origin/main...HEAD

@claude

claude Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review

What this PR does: Fixes fire-and-forget RSCRoute retry/refetch calls surfacing as unhandled promise rejections in production recover-on-error mode. The returned promise (handledRefetchPromise) still rejects so callers that await/.catch() it keep working exactly as before; when recoverOnError is true, an additional no-op .catch(() => undefined) is attached to the same promise object before it's returned. Since a promise only needs one attached handler (by the end of the microtask turn) to be marked handled by the runtime, this suppresses the unhandledRejection event regardless of whether the caller ever consumes the promise, without altering what callers observe.

Review findings: I traced this fix line-by-line, checked the removed/changed control flow against the previous behavior, and checked other call sites of refetch()/retry() in boundedCacheProvider.client.test.tsx and inlineRefreshButton.client.test.tsx to confirm existing .rejects.toThrow(...)-style assertions are unaffected (multiple handlers can coexist on the same promise). No correctness bugs, regressions, or security concerns found:

  • The extra handler is correctly gated on recoverOnError (production only), preserving the documented dev-mode-stays-loud behavior.
  • Ordering is correct: setRefetchErrorState runs synchronously inside the original .catch before the re-thrown error propagates to the new no-op handler, so state updates aren't affected by the fix.
  • The new regression test (1d4.) is well-targeted — it drives the retry through a real fire-and-forget UI handler (onClick={() => void retry()}, no .catch), asserts zero unhandledRejection events via a scoped process.on(...)/process.off(...) listener, and correctly restores NODE_ENV via the existing afterEach hook.
  • This is a general fix at the right altitude (inside RSCRoute's shared refetch implementation, which retry aliases) rather than a per-callsite bandaid, so it protects all current and future fire-and-forget callers of useCurrentRSCRoute().refetch()/retry().

No blocking issues. Nice, narrowly-scoped fix with good test coverage.

@justin808
justin808 added this pull request to the merge queue Jul 2, 2026
Merged via the queue into main with commit 83a4429 Jul 2, 2026
46 checks passed
@justin808
justin808 deleted the codex/batch-e-pro-route branch July 2, 2026 23:59
justin808 added a commit that referenced this pull request Jul 3, 2026
…cache-4317

* origin/main:
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)
  Fix visible hydration cleanup for detached roots (#4374)
  Avoid full locale default obsolete scans (#4398)
  Document RSC public-page validation and sidecar patterns (#4387)

# Conflicts:
#	CHANGELOG.md
justin808 added a commit that referenced this pull request Jul 3, 2026
…derer-shutdown-restart

* origin/main:
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)
  Fix visible hydration cleanup for detached roots (#4374)
  Avoid full locale default obsolete scans (#4398)
justin808 added a commit that referenced this pull request Jul 3, 2026
* origin/main:
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)
  Fix visible hydration cleanup for detached roots (#4374)
  Avoid full locale default obsolete scans (#4398)
  Document RSC public-page validation and sidecar patterns (#4387)
  Remove dead methods; prune always-false Rails<5.0 spec branches (#4418) (#4431)
justin808 added a commit that referenced this pull request Jul 3, 2026
…nsport

* origin/main:
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)

# Conflicts:
#	CHANGELOG.md
justin808 added a commit that referenced this pull request Jul 3, 2026
…-4364

* origin/main: (24 commits)
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)
  Fix visible hydration cleanup for detached roots (#4374)
  Avoid full locale default obsolete scans (#4398)
  Document RSC public-page validation and sidecar patterns (#4387)
  Remove dead methods; prune always-false Rails<5.0 spec branches (#4418) (#4431)
  Drop deprecation-tombstone config options for 17.0.0 (#4419) (#4432)
  Add mechanical parity guards for Ruby↔TS protocol constants (#4412) (#4427)
  Move Node tsconfigs from @tsconfig/node14 to @tsconfig/node18 (#4410) (#4429)
  [Pro] Remove unused addressable and rainbow runtime deps from gemspec (#4416) (#4422)
  Delete finished #3313 Prism Gemfile-rewriter spike (#4421)
  Extract generator scan/tracking helpers (#4405) (#4430)
  Extract install_dependency_group helper in JsDependencyManager (#4403) (#4424)
  Remove obsolete Ruby<2.6 YAML-aliases capability shim (#4417) (#4428)
  Remove inert config.server_render_method option (#4415) (#4423)
  Prune stale knip ignores and enforce binaries in CI (#4408) (#4425)
  Extract shared redux_store kwargs validator (#4402) (#4420)
  Avoid caching async props prerender streams (#4376)
  Release incremental render context on setup failure (#4383)
  ...

# Conflicts:
#	CHANGELOG.md
justin808 added a commit that referenced this pull request Jul 3, 2026
…370' into codex/batch-e-loadable-stats-retry-4371

* origin/codex/batch-e-rsc-parser-flush-4370:
  Add cached static RSC helper and diagnostics (#4386)
  Fix Pro tag revalidation retry after delete failures (#4375)
  Fix node renderer graceful shutdown restarts (#4400)
  Improve release-finish dry-run fetch handling (#4441)
  Flush RSC payloads before incomplete HTML tails (#4379)
  Handle sync RSC route failures as fetch errors (#4393)
  Delete never-wired RenderRequest/JsCodeBuilder/RenderingStrategy layer (#4414) (#4437)
  Delegate deprecated base/ shims to capabilities/ instead of cloning (#4413) (#4436)
  Remove pre-monorepo node-renderer devDep baggage; consolidate test multipart builders (#4435)
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)
justin808 added a commit that referenced this pull request Jul 7, 2026
)

## Summary

Adds user-visible `[Unreleased]` changelog entries for merged PRs since
`v17.0.0.rc.6` that prior passes missed, and fills in one missing PR
attribution. No version header is stamped (mainline `/update-changelog`,
no mode).

## Changes

**Attribution fix**
- `#### Changed` — the existing "`create-react-on-rails-app` now
defaults to Pro" entry had no PR link; attributed to [PR
4217](#4217).

**New entries**
- `#### Changed` — **[Pro]** Missing renderer password error now leads
with the local-development fix (`export RAILS_ENV=development
NODE_ENV=development`). [PR
4211](#4211), fixes
#4201.
- `#### Fixed` — `hydrate_on: visible` no longer leaks a detached root
or blocks re-hydrating a same-id replacement node. [PR
4374](#4374), fixes
#4328.
- `#### Fixed` — **[Pro]** RSCRoute error recovery now covers
synchronous route failures and fire-and-forget retries. [PR
4378](#4378) + [PR
4393](#4393), fixes
#4330 and #4372.
- `#### Removed` (new section) — **[Pro]** Removed the
`react-on-rails-pro/rscPayloadNode` export and `createRscPayloadNode`
helper; `RSCRoute` is the canonical integration. [PR
4440](#4440), fixes
#4439.

## Classification notes (reviewer trim points)

I swept every merged PR in `v17.0.0.rc.6..origin/main` (173 commits) and
cross-referenced each against the changelog. The overwhelming majority
were correctly omitted in prior passes (docs, CI, release-tooling,
benchmark harnesses, internal refactors, dev-dependency/Dependabot bumps
such as Shakapacker 10.2.0/10.3.0 and the npm-security group).

The five entries above are the ones I judged genuinely user-visible.
Three of them (#4211, #4374, #4378/#4393) were merged with a
`not_user_visible` ledger classification; I re-added them per the
changelog rubric (runtime bug fix / observable error-boundary behavior /
user-facing diagnostics). If you prefer to keep any of those out,
they're easy to drop.

## Verification

- `pnpm exec prettier --check CHANGELOG.md` → clean
- pre-commit hooks (trailing-newlines, markdown-links, prettier) →
passed
- File ends with a trailing newline


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Updated release notes to clarify an improved error message for missing
renderer passwords, including explicit environment setup guidance.
* Noted the removal of an outdated Pro RSC client-router export and
helper, with the newer routing approach identified as the standard.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
justin808 added a commit that referenced this pull request Jul 8, 2026
## Why

The 17.0 release notes and Pro docs had drift around the async-props
prerender-cache isolation fix and RC-only RSC payload helper removal.
The release train needs public-safe security/changelog framing,
refreshed Pro docs, regenerated LLM artifacts, and a recorded GHSA
decision.

Fixes #4494.
Fixes #4495.

## What Changed

- Promoted the async-props prerender-cache isolation note into the
staged `17.0.0.rc.7` security section with prerelease/stable-tag
framing; `Unreleased` remains empty while the rc.7 release section is
being assembled before publication.
- Recorded the GHSA/CVE decision in
`internal/analysis/2026-07-07-async-props-prerender-cache-security-decision.md`.
- Clarified Pro docs that `config.prerender_caching` skips async-props
renders and that async-props pages should use explicit fragment keys
only when safe.
- Updated the older RC payload-helper changelog entry to point final
users to the `RSCRoute` loader pattern.
- Backfilled or corrected staged rc.7 changelog coverage for #4392,
#4396, #4374, #4378, #4401, #4431, #4422, #4440, and #4447.
- Regenerated `llms-full-pro.txt`; `llms-full.txt` was refreshed on
`main` by #4509 and is no longer part of this branch after the rebase.
- Review fix: restored the `#### Fixed` heading after the new Security
section so unrelated fixes render under the correct heading.
- Rebased onto `origin/main` at `5933f8011`, keeping `main`'s newer
#4444/#4473 and #4422 changelog entries during conflict resolution.
- Review fix: folded the duplicate lower `#### Removed` changelog block
into the existing Unreleased Removed section so MD024 heading uniqueness
stays clean.

## Live Evidence

- `git tag --contains cdcba54` returned no tags.
- Visible GitHub releases still top out at `v17.0.0.rc.6`; `cdcba5438`
is on `origin/main` but not in that RC.
- Visible repository security advisories returned `[]` for all visible
and open states with the current token.
- The async-props feature commit appears only in prerelease tags
`v16.7.0.rc.0` through `v16.7.0.rc.3` and `v17.0.0.rc.0` through
`v17.0.0.rc.6`; it is not in stable `v16.6.0`.

## Validation

Current head: `d1a37aa6b4e4d186ecbced35304ba150afdc2330`.

- `.agents/bin/agent-workflow-seam-doctor` -> pass
- `node script/generate-llms-full.mjs` -> regenerated outputs
- `bash script/generate-llms-full-test.bash` -> 7 tests passed
- `node script/generate-llms-full.mjs --check` -> pass; `llms-full.txt`
1573 KiB, `llms-full-pro.txt` 579 KiB, 74 docs URLs and 8 sidebar
top-level sections validated
- `node script/generate-llms-full.mjs --validate` -> pass
- `pnpm exec prettier --check CHANGELOG.md docs/pro/fragment-caching.md
docs/pro/installation.md docs/pro/streaming-ssr.md
internal/analysis/2026-07-07-async-props-prerender-cache-security-decision.md`
-> pass
- `script/check-docs-sidebar` -> pass
- `git diff --check` -> pass after rebase
- `pnpm start format.listDifferent` -> pass after rebase and
duplicate-heading fix
- `pnpm exec markdownlint-cli2 CHANGELOG.md` -> unavailable locally
(`Command "markdownlint-cli2" not found`); duplicate `#### Removed`
heading removed by direct changelog inspection
- Review-fix pass: `codex review --base origin/main` -> no discrete
correctness issues found

## Review Gate

- Worker review and coordinator review: `codex review --base
origin/main` -> no discrete correctness issues found.
- Review threads for the changelog heading and generated-doc
source-drift comments were replied to and resolved.

## CI / Release Gate Notes

- Changelog classification: `changelog_present`.
- Workflow change audit: not applicable; this PR does not modify
`.github/workflows/**` or `.github/actions/**`.
- Local pre-commit/pre-push hook note: `branch-lint`, Prettier,
docs/sidebar, and llms checks passed where applicable, but local
`markdown-links` could not run because installed `lychee` v0.24.2 cannot
parse the repo `.lychee.toml` `include_fragments = false` value. Hosted
docs/link checks are requested for remote confirmation.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Breaking Changes**
* Removed undocumented Pro RSC helper methods and the RC-only
`rscPayloadNode` export; client-router loaders should return plain route
data and rendering should use `RSCRoute`.

* **Bug Fixes**
* Render helpers no longer mutate caller-provided options; refined
streaming retry diagnostics and improved behavior for detached-root
cleanup and fire-and-forget RSC retries.

* **Security**
* Clarified async-props prerender-cache isolation for specific
prerelease ranges.

* **Documentation**
* Updated streaming SSR, fragment caching, and installation guidance:
`config.prerender_caching` is skipped for async-props streaming renders.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-hosted-ci Run optimized hosted GitHub CI for this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Pro npm] RSCRoute retry()/refetch() always rethrows in production recoverOnError mode, causing unhandled promise rejections in natural error-UI usage

1 participant