Skip to content

Replace TurboError with structured error types and improve cold boot retry#231

Open
olivaresf wants to merge 4 commits intomainfrom
error0-fix-minimal
Open

Replace TurboError with structured error types and improve cold boot retry#231
olivaresf wants to merge 4 commits intomainfrom
error0-fix-minimal

Conversation

@olivaresf
Copy link
Member

@olivaresf olivaresf commented Feb 13, 2026

This PR replaces the generic TurboError enum with a structured HotwireNativeError type hierarchy that provides better error categorization, richer context, and smarter recovery behavior.

New Error Types

  • Add HotwireNativeError as the unified error type with three cases: .http(HTTPError), .web(WebError), and .load(LoadError).
  • Add HTTPError with exhaustive ClientError (4xx) and ServerError (5xx) cases, each with an isRetryable property (only 408, 429, 503, 504).
  • Add WebError wrapping URLError with semantic helpers: isOffline, isTimeout, isConnectionError, isSSLError.
  • Add LoadError for Turbo.js initialization failures: .notPresent, .notReady, .contentTypeMismatch, .invalidResponse.

RedirectHandler changes

  • Remove HTTP status code validation from RedirectHandler — any HTTP response proves the server is reachable.
  • Add 30-second timeout to RedirectHandler requests.
  • Cold boot retry on both reachable servers (.noRedirect) and network failures (.requestFailed), limited to one retry per visit.
  • Make retry handler conditional on error.isRetryable in Navigator.

Update JS Bridge

  • Update the JS bridge to split visitRequestFailedWithStatusCode into HTTP and non-HTTP paths, passing status codes to the native side.

===

Cold-boot retry (handled by hotwire-native)

When a Turbo.js visit fails with a non-HTTP status (error 0, timeout, etc.), the library now:

  1. Sends the same request via RedirectHandler to check server reachability
  2. If the server responds (any status code) → cold-boot retry (once)
  3. If the server is unreachable (timeout, DNS, connection refused) → cold-boot retry (once), then show the error

olivaresf and others added 4 commits February 13, 2026 00:47
Introduce HTTPError, WebError, LoadError, and HotwireNativeError
to provide richer, more descriptive error information throughout
the framework. These typed errors enable pattern matching and
give consumers actionable context about failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Pass statusCode from Turbo.js for non-HTTP status failures
- Add turboIsReady message to detect missing Turbo library earlier
- Add statusCode property to ScriptMessage
- Improve RedirectHandler with timeout, typed errors, and remove
  unnecessary status code validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all Error/TurboError usage with HotwireNativeError across
the core layers. Add retry-before-fail logic for transient JS
fetch failures and only offer retry handlers for retryable errors.
Remove the now-unused TurboError type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use pattern matching on HotwireNativeError in SceneController
- Add turboIsReady ScriptMessage tests
- Update SessionTests for dual notReady/notPresent load errors
- Adopt HotwireNativeError in test helpers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@olivaresf olivaresf marked this pull request as draft February 13, 2026 06:48
@olivaresf olivaresf mentioned this pull request Feb 13, 2026
@olivaresf olivaresf changed the title Error0 fix minimal Replace TurboError with structured error types and improve cold boot retry Feb 13, 2026
@olivaresf olivaresf requested a review from alpkeser February 13, 2026 07:30
case .pageLoaded:
pageLoadDelegate?.webView(self, didLoadPageWithRestorationIdentifier: message.restorationIdentifier!)
case .turboIsReady:
let isReady = message.data["isReady"] as? Bool ?? false

Choose a reason for hiding this comment

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

Nitpick: Like others, we could define a property for this on ScriptMessage.

Comment on lines +37 to +38
var request = URLRequest(url: location)
request.timeoutInterval = 30

Choose a reason for hiding this comment

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

As discussed with @olivaresf , if this lands in a HN release we'll need to a) investigate if we can make the turbo timeout and url request timeouts concurrent rather than seemingly additive and b) probably make this a config value for clients.

@olivaresf olivaresf marked this pull request as ready for review February 13, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants