Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.HttpClient
open Axial.PlatformService
open Axial.Process
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScriptReliability
This page shows how typed errors turn timeout and retry policy into ordinary, testable code.
Per-Request Timeouts
HttpClient.Timeout is one global setting that throws TaskCanceledException, indistinguishable from real
cancellation. An Axial timeout is per request and produces a dedicated typed error:
GET $"https://api.example.com/slow-report"
|> timeout (TimeSpan.FromSeconds 5.0)
|> fetch
// Fails with HttpError.TimedOut(request, 5s) — never confused with HttpError.Canceled.Retry Only Transient Failures
Retrying a 404 or a decode failure wastes time and can duplicate side effects. HttpError.isTransient
classifies exactly the failures where a retry can help: connection failures, timeouts, and 408/429/5xx statuses.
let users =
Http.getJson decodeUsers "https://api.example.com/users"
|> Http.retryTransient 4 (TimeSpan.FromMilliseconds 200.0)For full control, build the policy yourself and use the general Flow retry machinery:
let policy =
{ HttpError.transientPolicy 6 (TimeSpan.FromMilliseconds 100.0) with
ShouldRetry = fun error ->
HttpError.isTransient error
&& (match error with HttpError.Status r -> r.StatusCode <> 429 | _ -> true) }
workflow |> Flow.Runtime.retry policyworkflow |> Schedule.retry (Schedule.exponential (TimeSpan.FromMilliseconds 100.0) |> Schedule.jitteredWith random.NextDouble)Expected Statuses Are Part Of The Request
Reliability starts with saying what success means. The expectation travels with the request, so callers cannot forget to check:
DELETE $"https://api.example.com/users/{userId}"
|> expect [ 204; 404 ] // idempotent delete: already-gone is fine
|> fetchWhen Not To Retry
Do not wrap non-idempotent POSTs in retryTransient unless the server deduplicates requests (for example with an
idempotency key header): a timeout does not prove the server ignored the request. Send the key explicitly, then
retry safely:
POST $"https://api.example.com/payments"
|> header "Idempotency-Key" (Guid.NewGuid().ToString())
|> jsonBodyOf encodePayment payment
|> fetchJson decodeReceipt
|> withRetries 4
