How the Readiness Probe Determines When the Dev Server Is Ready in SXO

The readiness probe in SXO uses the waitForHttp function in src/js/cli/open.js to poll the development server's HTTP endpoint, treating any response with status codes 200–499 as "ready" while retrying with exponential backoff and jitter until the configured timeout expires.

The gc-victor/sxo repository implements a sophisticated readiness probe to determine exactly when the development server is ready to accept traffic. This mechanism ensures that browsers open only after the server is actually serving requests, preventing connection errors during the startup phase. The probe logic resides primarily in src/js/cli/open.js and combines intelligent HTTP polling with configurable retry strategies.

The Core Mechanism: waitForHttp and openWhenReady

The readiness determination is orchestrated by two primary functions exported from src/js/cli/open.js: openWhenReady and waitForHttp. The openWhenReady function serves as the high-level entry point that builds the target URL and initiates the probe, while waitForHttp implements the low-level polling logic.

Building the Target URL

Before probing begins, openWhenReady constructs the target URL using an internal buildUrl helper. As seen in lines 54–55 of src/js/cli/open.js, this combines the protocol, host, port, and pathname to form the complete endpoint that will be polled.

Abort Signal Handling

The probe respects external cancellation through the AbortSignal API. If an already-aborted signal is passed to openWhenReady, the function returns immediately without attempting any HTTP requests (lines 57–59). Throughout the polling loop, waitForHttp checks the signal state and terminates early if an abort is requested (lines 30–33).

How the Probe Determines "Ready" Status

The readiness probe employs a permissive status code check combined with intelligent request method fallback to determine when the server is accepting traffic.

Status Code Acceptance (200–499)

Inside waitForHttp, any HTTP response with a status code greater than or equal to 200 and less than 500 is treated as "ready" (lines 41–42). This includes successful responses (2xx), redirects (3xx), and client errors such as 404 Not Found. Notably, this means the probe considers the server ready even if the specific path returns a 404, as long as the server is responding. Only server-error responses (5xx) trigger the retry mechanism.

HEAD First, GET Fallback

By default (headFirst: true), the probe sends a HEAD request first to minimize bandwidth (lines 48–55). If the server responds with a ready status, the probe succeeds immediately. If the server returns 405 Method Not Allowed or 501 Not Implemented, the probe automatically falls back to a GET request (lines 56–59). This fallback logic ensures compatibility with servers that do not implement HEAD while maintaining efficiency for those that do.

Retry Logic and Timing

The readiness probe implements a sophisticated retry mechanism with exponential backoff, jitter, and adaptive per-request timeouts to balance responsiveness with system load.

Exponential Backoff with Jitter

When a request fails or returns a 5xx status, waitForHttp waits before retrying. The delay follows an exponential backoff pattern with a factor of approximately 1.7, capped at maxDelayMs (lines 25–28). To prevent thundering herd problems, the implementation adds a jitter of ±10% to each delay (lines 68–71). This randomized backoff ensures that multiple concurrent probes do not synchronize their retry attempts.

Per-Attempt Timeouts

Each individual HTTP request uses an adaptive timeout calculated as perAttempt (lines 44–46). This value ranges between 300 ms and 2 seconds, scaled based on the remaining overall timeout budget. By limiting the duration of any single request, the probe ensures that a slow or hanging connection does not exhaust the entire timeoutMs budget, allowing more retry attempts within the allocated time.

Implementation Examples

The following examples demonstrate how to use the readiness probe in practice.

Basic Usage with openWhenReady

// Open the browser when a dev server on port 3000 is ready
import { openWhenReady } from "./src/js/cli/open.js";

const result = await openWhenReady({
  port: 3000,
  pathname: "/",          // optional, defaults to "/"
  timeoutMs: 15000,      // wait up to 15 seconds
  verbose: true,         // enable debug logging of retries
});

console.log(result);
// → { opened: true, url: "http://localhost:3000/", timedOut: undefined }

Low-Level Probe with waitForHttp

// Use the low-level probe directly
import { waitForHttp } from "./src/js/cli/open.js";

const ready = await waitForHttp("http://localhost:3000", {
  timeoutMs: 8000,
  headFirst: true,      // default – try HEAD first
  verbose: false,
});

if (ready) {
  console.log("Dev server is up!");
} else {
  console.warn("Server did not become ready in time.");
}

Aborting the Probe

// Abort the probe after an external signal
import { openWhenReady } from "./src/js/cli/open.js";

const controller = new AbortController();
setTimeout(() => controller.abort(), 5000); // abort after 5s

const result = await openWhenReady({
  port: 3000,
  signal: controller.signal,
});

console.log(result);
// → { opened: false, url: "http://localhost:3000/", timedOut: false, error: "aborted" }

Summary

The readiness probe in SXO determines when the development server is ready through a sophisticated HTTP polling mechanism:

  • Permissive status checking: Accepts HTTP 200–499 (including 404s) as "ready", retrying only on 5xx errors
  • Method fallback: Attempts HEAD requests first, falling back to GET if the server returns 405 or 501
  • Intelligent retry logic: Implements exponential backoff with 1.7x factor, ±10% jitter, and adaptive per-request timeouts (300ms–2s)
  • Abort support: Respects AbortSignal for graceful cancellation
  • Entry points: openWhenReady for high-level browser opening, waitForHttp for low-level probing

Frequently Asked Questions

What HTTP status codes does the SXO readiness probe accept as "ready"?

The probe treats any response with a status code greater than or equal to 200 and less than 500 as indicating the server is ready. This includes successful responses (2xx), redirects (3xx), and client errors such as 404 Not Found. Only server-error responses (5xx) trigger the retry mechanism, as implemented in lines 41–42 of src/js/cli/open.js.

How does the probe handle servers that don't support HEAD requests?

By default, the probe sends a HEAD request first to minimize bandwidth. If the server responds with 405 Method Not Allowed or 501 Not Implemented, the probe automatically falls back to a GET request. This fallback logic ensures compatibility with servers that do not implement HEAD while maintaining efficiency for those that do, as seen in lines 48–59 of src/js/cli/open.js.

Can I configure the timeout and retry behavior?

Yes, the probe accepts several configuration options. You can set timeoutMs to control the total waiting time, and the internal retry logic uses an exponential backoff factor of approximately 1.7 with ±10% jitter and a cap on maximum delay. Additionally, each individual request uses an adaptive timeout between 300 ms and 2 seconds based on remaining time budget, ensuring efficient resource usage.

What happens if the readiness probe is aborted mid-check?

The probe respects the AbortSignal API for graceful cancellation. If an abort signal is triggered—either before the probe starts (lines 57–59) or during the retry loop (lines 30–33)—the waitForHttp function immediately stops polling and returns false. The openWhenReady wrapper then returns an object indicating the failure reason, including { opened: false, error: "aborted" }, allowing calling code to handle cancellation appropriately.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →