juspay/hyperswitch · error · Error

Failed to fetch QR code image: ${response.statusText}

Error message

Failed to fetch QR code image: ${response.statusText}

What it means

Thrown by fetchAndParseQRCode in cypress-tests-v2 redirection support. It fetches the QR image URL (from a UPI intent response's qrInfoData.qrLink/qr field) with the browser fetch API and throws when response.ok is false, embedding response.statusText. This is a test-side network assertion, not app code.

Source

Thrown at cypress-tests-v2/cypress/support/redirectionHandler.js:413

      // No CORS workaround needed
      cy.window().its("location.origin").should("eq", expected_url.origin);
    } else {
      // Workaround for CORS to allow cross-origin iframe
      cy.origin(
        expected_url.origin,
        { args: { expected_url: expected_url.origin } },
        ({ expected_url }) => {
          cy.window().its("location.origin").should("eq", expected_url);
        }
      );
    }
  }
}

async function fetchAndParseQRCode(url) {
  const response = await fetch(url, { encoding: "binary" });
  if (!response.ok) {
    throw new Error(`Failed to fetch QR code image: ${response.statusText}`);
  }
  const blob = await response.blob();
  const reader = new FileReader();
  return await new Promise((resolve, reject) => {
    reader.onload = () => {
      const base64Image = reader.result.split(",")[1]; // Remove data URI prefix
      const image = new Image();
      image.src = base64Image;

      image.onload = () => {
        const canvas = document.createElement("canvas");
        const ctx = canvas.getContext("2d");
        canvas.width = image.width;
        canvas.height = image.height;
        ctx.drawImage(image, 0, 0);

        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const qrCodeData = jsQR(

View on GitHub (pinned to 9b8b89dc37)

Solutions

  1. Fetch the QR immediately after receiving the payment response, before any cy.wait, so the signed link has not expired
  2. Manually curl/GET the failing URL from the same CI environment to see the real status code and body
  3. Re-run the payment initiation to obtain a fresh QR link before parsing
  4. Verify the URL used comes from response.body.qrInfoData.qrLink (or qr) exactly, with no truncation or encoding issues

Example fix

// before
const response = await fetch(url, { encoding: 'binary' });
if (!response.ok) {
  throw new Error(`Failed to fetch QR code image: ${response.statusText}`);
}

// after (retry once with a fresh short-lived link in mind)
async function fetchQr(url, attempts = 2) {
  for (let i = 0; i < attempts; i++) {
    const response = await fetch(url, { encoding: 'binary' });
    if (response.ok) return response;
    if (i === attempts - 1) {
      throw new Error(`Failed to fetch QR code image: ${response.status} ${response.statusText}`);
    }
    await new Promise((r) => setTimeout(r, 2000));
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// Before parsing, confirm the QR link exists and looks fetchable
const qrLink = response.body.qrInfoData?.qrLink || response.body.qrInfoData?.qr;
if (!qrLink || !/^https?:\/\//.test(qrLink)) {
  throw new Error(`No QR link to parse (qrInfoData: ${JSON.stringify(response.body.qrInfoData)})`);
}

Try / catch

// Short-lived signed QR links: retry briefly, then fail with the status
async function fetchQrWithRetry(url, attempts = 3) {
  let lastErr;
  for (let i = 0; i < attempts; i++) {
    const response = await fetch(url, { encoding: 'binary' });
    if (response.ok) return response;
    lastErr = new Error(`Failed to fetch QR code image: ${response.status} ${response.statusText}`);
    await new Promise((r) => setTimeout(r, 2000));
  }
  throw lastErr;
}

Prevention

When it happens

Trigger: cy flows that parse a UPI intent QR: the fetch of the QR image URL returns 404 (session/link expired), 401/403 (signed URL or auth issue), or 5xx from the QR service — response.ok is false and the error carries the status text.

Common situations: The QR link is short-lived and the test reached it after expiry (slow CI, retries, cy.wait delays); the environment's QR host is unreachable from the CI runner (DNS/proxy/firewall); baseUrl routing differences between sandbox and integ so the QR URL points at the wrong host.

Related errors


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