juspay/hyperswitch · error · Error
Customer create call failed with status: ${response.status}
Error message
Customer create call failed with status: ${response.status} and message: ${response.body?.error?.message} What it means
Thrown by the customerCreateCall custom Cypress command after POSTing a customer-create request with failOnStatusCode-style handling: status 200 goes through field-echo assertions (e.g. phone_country_code), status 400 is tolerated only when the message contains 'already exists' (asserting code IR_12 and the exact message), and every other status falls into the else-branch throw with status and error.message. It means the Customer API responded with a status the command does not consider a known-good outcome.
Source
Thrown at cypress-tests/cypress/support/commands.js:8328
expect(customerCreateBody.metadata, "metadata").to.deep.equal(
response.body.metadata
);
expect(customerCreateBody.address, "address").to.deep.equal(
response.body.address
);
expect(
customerCreateBody.phone_country_code,
"phone_country_code"
).to.equal(response.body.phone_country_code);
} else if (response.status === 400) {
if (response.body.error.message.includes("already exists")) {
expect(response.body.error.code).to.equal("IR_12");
expect(response.body.error.message).to.equal(
"Customer with the given `customer_id` already exists"
);
}
} else {
throw new Error(
`Customer create call failed with status: ${response.status} and message: ${response.body?.error?.message}`
);
}
});
}
);
// Payment Methods Commands (v2 code - port 8082)
Cypress.Commands.add("paymentMethodCreateCall", (globalState, pmData) => {
const apiKey = globalState.get("apiKey");
const profileId = globalState.get("profileId");
const customerId = globalState.get("customerId");
const requestBody = {
...pmData,
customer_id: customerId,
};
View on GitHub (pinned to 9b8b89dc37)
Solutions
- Parse the status and message out of the thrown text: 401 -> refresh/regenerate the API key in globalState; 403 -> check the key's access to the profile; 404 -> verify base URL and customers endpoint path.
- If 400: diff the actual message against 'Customer with the given `customer_id` already exists' — if it is a different validation error, fix the offending field (phone_country_code, email, customer_id format) in the customer fixture.
- Generate a unique customer_id per run (e.g. suffix with Date.now()/UUID) so the already-exists path only occurs when you intentionally test it.
- If the message text changed in a new server version, update the command's expected string/code (IR_12) to match the current contract.
- If 429/5xx: add retry with backoff or wait for service health before rerunning.
Example fix
// before: fixed customer_id, collisions fall into the throw on message drift
cy.customerCreateCall(globalState, { ...pmData, customer_id: 'cust_test_1' });
// after: unique id per run; 400 branch only reached deliberately
const customerId = `cust_${Cypress.moment().format('x')}_${Cypress.spec.name}`;
cy.customerCreateCall(globalState, { ...pmData, customer_id: customerId }); Defensive patterns
Strategy: validation
Validate before calling
// Run before customerCreateCall
function validateCustomerCreateCall(globalState, customerData) {
if (!globalState.get('apiKey')) throw new Error('customerCreateCall: apiKey missing in globalState');
if (!globalState.get('profileId')) throw new Error('customerCreateCall: profileId missing in globalState');
if (!customerData?.customer_id) throw new Error('customerCreateCall: customer_id required');
if (!/^[A-Za-z0-9_-]+$/.test(customerData.customer_id)) {
throw new Error(`customerCreateCall: invalid customer_id '${customerData.customer_id}'`);
}
} Type guard
// Distinguish the tolerated 409-style 'already exists' body from other failures
function isAlreadyExistsError(body) {
return (
typeof body === 'object' &&
body !== null &&
body.error?.code === 'IR_12' &&
typeof body.error?.message === 'string' &&
body.error.message.includes('already exists')
);
} Try / catch
// Prefer result-object returns over throws so callers branch instead of catching:
// const r = await customerCreate(state, data);
// if (r.status === 400 && isAlreadyExistsError(r.body)) { /* reuse or regenerate id */ }
// For the existing throwing command, callers in plain Node wrappers can do:
// try { ... } catch (e) {
// const m = e.message.match(/status: (\d+) and message: (.*)$/);
// if (!m) throw e;
// const [, status, msg] = m;
// if (status === '400' && !msg.includes('already exists')) throw new Error(`payload invalid: ${msg}`);
// if (status === '401') throw new Error('refresh API key');
// } Prevention
- Generate a unique customer_id per run (timestamp/UUID suffix) so the 400 branch is only hit intentionally.
- Set apiKey/profileId in globalState once in a before() hook and assert they exist before any create call.
- Pin the base URL per environment and never mix keys from one env with URLs from another.
- When upgrading the server, re-check the exact 'already exists' message and IR_12 code the command asserts.
- Log x-request-id from responses so failures can be traced in server logs.
When it happens
Trigger: 401 with a bad/expired API key (globalState 'apiKey'); 403 when the key cannot access the profile (globalState 'profileId'); 404 from a wrong base URL or endpoint path; 400 with any message other than 'Customer with the given `customer_id` already exists' (e.g. invalid phone_country_code, invalid email format); 429 rate limiting; 5xx server errors.
Common situations: Reusing a hardcoded customer_id across runs and hitting a 400 whose message differs slightly from the asserted string; API key expired or belonging to another environment; base URL/port pointing at the wrong service version; profile not provisioned; server contract change altering the already-exists message so the 400 branch no longer matches and 400 now falls through to the throw.
Related errors
- Webhook failed with error code "${response.body?.error?.code
- Payment method create failed with status ${response.status}:
- Unsupported connector: ${connectorId}
- Unsupported payment method type: ${payment_method_type}
- Failed to fetch QR code image: ${response.statusText}
AI-assisted analysis of juspay/hyperswitch@9b8b89dc37 (2026-08-16).
Data as JSON: /api/errors/d7693534608b0a38.
Report an issue: GitHub.