nikivdev/code · error

✗ Unhealthy: expected HTTP {}, got {} ({:.2}s)

Error message

✗ Unhealthy: expected HTTP {}, got {} ({:.2}s)

What it means

The host was reachable but the HTTP status code returned by curl differs from the expected status (default 200). The error reports expected vs actual status and elapsed time so you can see what the server answered.

Source

Thrown at src/deploy.rs:3188

        .output()
        .context("Failed to run curl")?;

    let elapsed = start.elapsed();
    let status_str = String::from_utf8_lossy(&output.stdout);
    let actual_status: u16 = status_str.trim().parse().unwrap_or(0);

    if actual_status == expected_status {
        println!(
            "✓ Healthy (HTTP {} in {:.2}s)",
            actual_status,
            elapsed.as_secs_f64()
        );
        Ok(())
    } else if actual_status == 0 {
        let stderr = String::from_utf8_lossy(&output.stderr);
        bail!("✗ Unreachable: {}", stderr.trim());
    } else {
        bail!(
            "✗ Unhealthy: expected HTTP {}, got {} ({:.2}s)",
            expected_status,
            actual_status,
            elapsed.as_secs_f64()
        );
    }
}

View on GitHub (pinned to a747e741ae)

Solutions

  1. Inspect the returned status: if 5xx, check the server logs for a crash or bad upstream port
  2. If 404, verify the URL points at a route that exists (use --url with the right path)
  3. If the endpoint intentionally returns a different code, set the expected status for the check accordingly
  4. If the app was still booting, wait and rerun the health check

Example fix

// before
$ flow check --url https://app.example.com/api   # 404, route doesn't exist

// after
$ flow check --url https://app.example.com/health
Defensive patterns

Strategy: retry

Validate before calling

// verify the endpoint returns the expected code first
actual=$(curl -s -o /dev/null -w '%{http_code}' "$URL")
[ "$actual" = "200" ] || echo "warning: $URL returns $actual"

Try / catch

// tolerate transient 502/503 during rollout, retry N times
match check_health(url, 200) {
    Err(e) if is_transient_5xx(&e) => retry_with_backoff(3, || check_health(url, 200)),
    other => other,
}

Prevention

When it happens

Trigger: The deployed app answers with a non-expected status: 404 (wrong path/root route missing), 502/503 (upstream or still booting), 401/403 (auth required), or the check expects a status the endpoint never returns.

Common situations: Deployment finished but app still starting (503); reverse proxy returning 502 because the upstream port is wrong; API requires auth so root returns 401; expected_status misconfigured for an endpoint that redirects.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/ddd691a647ab6304. Report an issue: GitHub.