Code review example: Hallucinated API: axios has no retries option, so nothing ever retries in Node

Retired from the DiffDojo practice pool in October 2026; the review below is the canonical answer. An AI wrote this pull request for the task underneath. Read the diff and decide what you would say before you scroll to the review.

Node · Hallucinated APIs & dependencies · intermediate

The task

Retry flaky payment-status calls

  1. Fetch payment status from the provider with GET, using axios.
  2. The provider is flaky, so retry a failed request up to 3 times before giving up.
  3. Throw if it still fails after the retries.

Adds automatic retries to the payment-status call. Passes axios a retries option so a failed request is retried up to 3 times before it throws. Confirmed the happy path returns the status object.

Called during checkout. A shared setup module sets axios.defaults.baseURL to the provider, so the relative path resolves; the provider intermittently returns 503s under load.

The diff

src/payments/status.js+10
1+ const axios = require('axios');
2+
3+ async function getPaymentStatus(paymentId) {
4+ const res = await axios.get(`/payments/${paymentId}/status`, {
5+ retries: 3,
6+ });
7+ return res.data;
8+ }
9+
10+ module.exports = { getPaymentStatus };

The review

Issue 1: Hallucinated API: axios has no retries option, so nothing ever retries

src/payments/status.js, lines 4, 5, 6

  1. retries is not a real axios option. Retrying comes from a separate package (axios-retry) or a hand-rolled loop, axios's request config has no such key.
  2. Axios accepts the unknown key silently. It preserves the extra entry on the config object but attaches no retry behavior to it, so the code compiles, runs, and returns the status on the happy path, exactly why it passes a quick test.
  3. There is zero retry logic. The very first 503 rejects the promise and getPaymentStatus throws, defeating the whole point of the change.

The fix: use axios-retry, or wrap the call in an explicit retry loop (one initial attempt plus up to three retries) with a small backoff, and never trust a config key just because it reads plausibly; check the library's actual API.

The fix

const axios = require('axios');

async function getPaymentStatus(paymentId) {
  let lastErr;
  // One initial attempt plus up to three retries.
  for (let attempt = 0; attempt <= 3; attempt++) {
    try {
      const res = await axios.get(`/payments/${paymentId}/status`);
      return res.data;
    } catch (err) {
      lastErr = err;
    }
  }
  throw lastErr;
}

module.exports = { getPaymentStatus };

Why it matters

A plausible-sounding config option → ask: "does this key actually exist in the library's API?"

Real-world stakes: this is the signature failure of AI-generated code, an option that doesn't exist but reads exactly like a real axios feature. It passes review at a glance and passes tests because the happy path never needs a retry. Then in production the flaky provider returns its first 503, the call throws instead of retrying, and checkouts fail under exactly the load the retry was meant to survive, while the code looks like it does retry.

Read more: axios retry request option, axios-retry package, axios ignores unknown config options, hallucinated api llm nonexistent method

The verdict

Request changes. Hallucinated API: axios has no retries option, so nothing ever retries

Try a live one

Node PRs you have not seen the answer to.

Leave inline comments and a verdict, then see what you caught. Free, no signup.

Review a Node PR →

How to review Node code · All code review examples