Transaction is being retried by relayer despite throwing DefenderApiResponseError 400 error

I'm using the Defender relayer to perform a transaction on a polygon contract.
It works smoothly 99% of the time however we're experiencing some very odd behavior when it does fail.

We got relayer funds low alert at the time but it could have been also the 120 tx/hour limit.
The relayer was topped up, and at no point there was not enough MATIC to perform the transaction.

However, our app did receive the following errors
Request failed with status code 400 DefenderApiResponseError: Request failed with status code 400

at rejectWithDefenderApiError (/workspace/node_modules/defender-base-client/lib/api/api.js:11:27)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async /workspace/node_modules/defender-relay-client/lib/api/index.js:92:21
    at async ApiRelayer.apiCall (/workspace/node_modules/defender-base-client/lib/api/client.js:26:20)
    at async DefenderRelaySigner.sendTransaction (/workspace/node_modules/defender-relay-client/lib/ethers/signer.js:122:15)

This was caught by our app, communicated to the user, and was attempted later and succeeded.
However, to our surprise it looks like this was a false negative, because all of those transactions did go through which is very very bad (imagine sending double tokens!)

Is this a bug on your end?
Is this expected behavior? How can prevent this from happening and trust that an error means failed, and if it's not failed how can I get the tx hash of this transaction.


:computer: Environment
OpenZeppelin Defender relayer on Polygon using ethers.js and DefenderRelayProvider

For example, the below transaction was sent when my app received an error back

Later the action was attempted again by the client (slighly different tokens selected, but some were the same and the ended up with double spend)

For the 0xf5af6f340367b4e83f1fcd98c866d023ed0d0c3cdf9cad25849392178ec4ff51 transaction I received the error above and assumed it failed (didn't get the tx hash back but an exception)

:1234: Code to reproduce

const assetsContract = await getAssetsContract();
    const minterWallet = await getMinterWallet();

    const contractWithSigner = assetsContract.connect(minterWallet);

    const transaction = await contractWithSigner.mintToMultipleWallets(

    return transaction.hash;

Where assetsContract is :

const credentials = {
      apiKey: RELAYER_API_KEY,
      apiSecret: RELAYER_SECRET_KEY,

    const provider = new DefenderRelayProvider(credentials);

    const assets = new Contract(

    return assets;

Minter wallet is:

const credentials = {
      apiKey: RELAYER_API_KEY,
      apiSecret: RELAYER_SECRET_KEY,

    const provider = new DefenderRelayProvider(credentials);
    const signer = new DefenderRelaySigner(credentials, provider, {
      speed: config.web3_transaction_speed,

    return signer;

Hi @Jon_Achai ,

Your error indicates that the request sent to the server was malformed or invalid, and the server could not understand it. The error could be due to a wrong URL, a missing or incorrect parameter, or an authorization issue. To fix this error, you would need to check the request and response objects to identify what went wrong, and correct the issue. This is not a Defender error, but a client error.

It's hard to say what the core issue is without having a full view into the application, but this sounds like an error handling issue on the application side of things.