juspay/hyperswitch · error

Invalid HEX color: "${hex}"

Error message

Invalid HEX color: "${hex}"

What it means

Identical vendored invert-color code, but embedded in the payment link status page (crates/router/src/core/payment_link/payment_link_status/status.js). hexToRgbArray() throws when, after removing a leading '#', the string is not exactly 3 or 6 hex digits. This page re-renders branding colours while polling the payment status, so an invalid branding hex breaks the status view.

Source

Thrown at crates/router/src/core/payment_link/payment_link_status/status.js:29

  } catch (e) {
    console.error("Error decoding and parsing string URI:", e);
    return uri;
  }
}

/**
 * Ref - https://github.com/onury/invert-color/blob/master/lib/cjs/invert.js
 */
function padz(str, len) {
  if (len === void 0) {
    len = 2;
  }
  return (new Array(len).join("0") + str).slice(-len);
}
function hexToRgbArray(hex) {
  if (hex.slice(0, 1) === "#") hex = hex.slice(1);
  var RE_HEX = /^(?:[0-9a-f]{3}){1,2}$/i;
  if (!RE_HEX.test(hex)) throw new Error('Invalid HEX color: "' + hex + '"');
  if (hex.length === 3) {
    hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
  }
  return [
    parseInt(hex.slice(0, 2), 16),
    parseInt(hex.slice(2, 4), 16),
    parseInt(hex.slice(4, 6), 16),
  ];
}
function toRgbArray(c) {
  if (!c) throw new Error("Invalid color value");
  if (Array.isArray(c)) return c;
  return typeof c === "string" ? hexToRgbArray(c) : [c.r, c.g, c.b];
}
function getLuminance(c) {
  var i, x;
  var a = [];
  for (i = 0; i < c.length; i++) {

View on GitHub (pinned to 9b8b89dc37)

Solutions

  1. Fix the stored branding value to a strict 3/6-digit hex on the business/merchant profile or the payment link
  2. Apply the same defensive default in status.js as in payment_link.js before calling invert()
  3. Add hex validation where branding is authored so both pages stop receiving bad values
  4. Keep the two vendored copies in sync — a fix applied to only one file leaves the other throwing

Example fix

// before (status.js)
var contrastBWColor = invert(primaryColor, true);

// after
var HEX_RE = /^#?(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
var primaryColor =
  typeof theme === 'string' && HEX_RE.test(theme.trim()) ? theme : '#1a2b3c';
var contrastBWColor = invert(primaryColor, true);
Defensive patterns

Strategy: validation

Validate before calling

const HEX_RE = /^#?(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
function normalizeHexColor(c, fallback = '#1a2b3c') {
  return typeof c === 'string' && HEX_RE.test(c.trim()) ? c.trim() : fallback;
}
// use in status.js render path:
var theme = normalizeHexColor(statusPayload.theme);

Type guard

/** @param {unknown} c @returns {c is string} */
function isHexColor(c) {
  return typeof c === 'string' && /^#?(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test(c.trim());
}

Try / catch

try {
  contrastBWColor = invert(theme, true);
} catch (e) {
  if (/Invalid HEX color/.test(e.message)) {
    contrastBWColor = '#ffffff';
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Opening the payment link status page for a link whose theme or payment_button_colour is a named colour ('blue'), an rgb()/rgba() string, 4/8-digit hex, or contains whitespace/invalid characters; invert() is invoked on those values during status-page colour setup.

Common situations: Same root cause as the initiate page: branding values persisted without hex validation; values copied from CSS or a design tool ('#1A2B3CCC' from Figma exports alpha hex); inconsistent data between the initiate and status payloads after a partial fix on only one page.

Related errors


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