juspay/hyperswitch · error · Error
Expected no deny action for non-blocklisted card
Error message
Expected no deny action for non-blocklisted card
What it means
Thrown by paymentsEligibilityCheck when POST /payments/{paymentId}/eligibility returns 200 but the response contains sdk_next_action.next_action.deny while the fixture Response does not expect one. The command treats presence of a deny action for a supposedly non-blocklisted card as a test failure (blocklist leaking onto clean cards).
Source
Thrown at cypress-tests/cypress/support/commands.js:7829
.to.have.property("payment_id")
.to.equal(paymentId);
// Check for expected response structure based on test case
if (resData.body.sdk_next_action?.next_action?.deny) {
expect(response.body).to.have.property("sdk_next_action");
expect(response.body.sdk_next_action).to.have.property(
"next_action"
);
expect(response.body.sdk_next_action.next_action).to.have.property(
"deny"
);
expect(response.body.sdk_next_action.next_action.deny)
.to.have.property("message")
.to.equal(resData.body.sdk_next_action.next_action.deny.message);
} else {
// For non-blocklisted cards, we expect no deny action
if (response.body.sdk_next_action?.next_action?.deny) {
throw new Error(
"Expected no deny action for non-blocklisted card"
);
}
}
} else {
throw new Error(
`Eligibility check failed with status: ${response.status} and message: ${response.body?.error?.message}`
);
}
});
});
}
);
// DDC Race Condition Test Commands
Cypress.Commands.add(
"ddcServerSideRaceConditionTest",
(confirmData, globalState) => {View on GitHub (pinned to 9b8b89dc37)
Solutions
- Clean up blocklist rules in an after() hook (cy.blocklistDeleteRule) for every spec that creates them
- Use distinct card BINs for deny and allow eligibility tests to avoid rule overlap
- If the connector legitimately denies, add sdk_next_action.next_action.deny.message to the fixture Response
- Verify via GET /blocklist that no unexpected rule covers the card being tested
Example fix
// before
after(() => {
// no cleanup → stale card_bin rule denies later specs
});
// after
after(() => {
cy.blocklistDeleteRule('card_bin', blocklistedBin, globalState);
}); Defensive patterns
Strategy: validation
Validate before calling
// precondition: no lingering rules should cover the card under test
const cardBin = body.payment_method_data.card_number.slice(0, 6);
cy.request({
method: 'GET',
url: `${globalState.get('baseUrl')}/blocklist?data_kind=card_bin`,
headers: { 'api-key': globalState.get('apiKey') },
failOnStatusCode: false,
}).then((res) => {
const covered = res.status === 200 && JSON.stringify(res.body).includes(cardBin);
expect(covered, 'no stale blocklist rule covers this card').to.be.false;
}); Type guard
const hasDenyAction = (b) => Boolean(b?.sdk_next_action?.next_action?.deny);
Prevention
- Use disjoint card BINs for deny vs allow eligibility tests
- Delete blocklist rules in after() hooks in the same spec that created them
- Keep deny-expectation fixtures explicit by including sdk_next_action.next_action.deny.message
When it happens
Trigger: The card used in the eligibility check matches a blocklist rule left over from an earlier test/spec (overlapping BIN); server-side false positive denying a clean card; fixture Response block missing the deny structure while the connector legitimately returns one.
Common situations: Shared test card BINs across specs with rules created in one spec still active during another; parallelized specs racing blocklist create/delete; expected-deny fixtures written without the sdk_next_action.next_action.deny node.
Related errors
- Expecting valid response but got an error response
- Invalid authentication type ${response.body.authentication_t
- Blocklist create rule failed with status: ${response.status}
- Blocklist delete failed with status: ${response.status} and
- Eligibility check failed with status: ${response.status} and
AI-assisted analysis of juspay/hyperswitch@9b8b89dc37 (2026-08-16).
Data as JSON: /api/errors/c8f9aed7560a29ca.
Report an issue: GitHub.