juspay/hyperswitch · error · Error

Health Check failed with status: `${response.status}` and bo

Error message

Health Check failed with status: `${response.status}` and body: `${response.body}`

What it means

Thrown by the healthCheck Cypress command in cypress-tests/cypress/support/commands.js: it GETs the router's health endpoint (Accept: application/json) and, unless status is 200 with body exactly 'health is good', throws with the actual status and body. It is the readiness probe tests call before touching the environment under test.

Source

Thrown at cypress-tests/cypress/support/commands.js:602

Cypress.Commands.add("healthCheck", (globalState) => {
  const baseUrl = globalState.get("baseUrl");
  const url = `${baseUrl}/health`;

  cy.request({
    method: "GET",
    url: url,
    headers: {
      Accept: "application/json",
    },
  }).then((response) => {
    logRequestId(response.headers["x-request-id"]);

    cy.wrap(response).then(() => {
      if (response.status === 200) {
        expect(response.body).to.equal("health is good");
      } else {
        throw new Error(
          `Health Check failed with status: \`${response.status}\` and body: \`${response.body}\``
        );
      }
    });
  });
});

/**
 * Creates a merchant account and optionally validates response fields.
 * @param {Object} merchantCreateBody - The merchant creation request body
 * @param {Object} globalState - The global state object
 * @param {Object} options - Options for merchant creation
 * @param {string|null} [options.expectedMerchantAccountType=null] - Expected merchant_account_type to validate (optional)
 * @param {string} [options.expectedProductType="orchestration"] - Expected product_type to validate. Pass a string value like "vault", "recon", "recovery", etc. to validate the response contains this product_type. Defaults to "orchestration".
 * @param {number} [options.expectedStatus=200] - Expected HTTP status code. Use 400 for negative test cases.
 * @param {string} [options.expectedErrorCode=null] - Expected error code in response body (for negative test cases, e.g. "IR_06")
 * @param {string} [options.merchantIdStateKey="merchantId"] - Key to store merchant ID in global state
 * @param {string} [options.profileIdStateKey="profileId"] - Key to store profile ID in global state

View on GitHub (pinned to 9b8b89dc37)

Solutions

  1. Confirm the target service is up: curl <baseUrl>/health manually and expect 200 'health is good'
  2. Fix the baseUrl/port in globalState if it points at the wrong place (the body in the message usually reveals a proxy error page)
  3. In CI, add a readiness wait before invoking Cypress so healthCheck is a verification, not the first probe
  4. Check service logs for startup failures (DB unreachable, pending migrations) if health keeps failing

Example fix

// before
cy.healthCheck(url); // one-shot, throws while service boots

// after (retry until ready)
Cypress.Commands.add('healthCheckWithRetry', (url, attempts = 10) => {
  const attempt = (n) =>
    cy.request({ method: 'GET', url, failOnStatusCode: false }).then((response) => {
      if (response.status === 200 && response.body === 'health is good') return;
      if (n <= 1) {
        throw new Error(`Health Check failed with status: ${response.status} and body: ${response.body}`);
      }
      cy.wait(3000).then(() => attempt(n - 1));
    });
  return attempt(attempts);
});
Defensive patterns

Strategy: retry

Validate before calling

// Probe readiness before the assertion-style healthCheck
cy.request({ method: 'GET', url: `${baseUrl}/health`, failOnStatusCode: false }).then((response) => {
  if (response.status !== 200) {
    throw new Error(`Service not ready (${response.status}); wait for deployment to finish before running tests`);
  }
});

Try / catch

// Readiness-style retry around the strict one-shot check
Cypress.Commands.add('healthCheckWithRetry', (url, attempts = 10) => {
  const attempt = (n) =>
    cy.request({ method: 'GET', url, failOnStatusCode: false }).then((response) => {
      if (response.status === 200 && response.body === 'health is good') return;
      if (n <= 1) {
        throw new Error(`Health Check failed with status: \`${response.status}\` and body: \`${response.body}\``);
      }
      cy.wait(3000).then(() => attempt(n - 1));
    });
  return attempt(attempts);
});

Prevention

When it happens

Trigger: healthCheck runs while the router service is still starting, crashed, or misconfigured (DB/migrations pending) so /health returns 5xx; or the baseUrl in globalState points at the wrong host/port so the response is a proxy/gateway error page (status 404/502 with an HTML body).

Common situations: CI pipeline starts Cypress before the service is up (no readiness gate); local run against a stopped container; port mismatch after a compose change; k8s liveness not yet passing during rollout; smoke tests against an environment mid-deploy.

Related errors


AI-assisted analysis of juspay/hyperswitch@9b8b89dc37 (2026-08-16). Data as JSON: /api/errors/c28e7787b5956197. Report an issue: GitHub.