How to Fix a “Module not found” Error with Axios and Follow Redirect in Next.js

I recently ran into a frustrating error while building a Next.js 13 app. I was using axios to fetch API data inside getStaticProps(), but during the build, my app threw a “Module not found: Can’t resolve ‘debug’ in ‘…/node_modules/follow-redirects’” error.

At first, I thought reinstalling packages would fix it. But the deeper I dug, I realized there were actually two separate issues:

  1. A resolver error involving follow-redirects → debug.
  2. Incorrect usage of getStaticProps()—and even the wrong data-fetching model if you’re on the new App Router.

Let me break down my journey, the fixes, and some “bonus practice” functionality that made my project more resilient.

The First Code

Here’s the original code I wrote inside getStaticProps:

// First attempt (WRONG)
export async function getStaticProps() {
  await axios.get('https://my-api-url.com/getlist')
    .then(response => {
      setData(response.data);
      setLoading(false);
    })
    .catch(error => {
      console.error('Error fetching data:', error);
      setLoading(false);
    });

  const data = await response.json();
  console.log(data);
}

What Wrong Here

  • The “Module not found” error
    axios uses the Node adapter follow-redirects, which has an optional dependency on debug. In some Webpack/Next.js builds, that optional dependency still gets resolved at build time. If debug isn’t installed, you’ll see this error.
  • Misuse of getStaticProps
    • getStaticProps runs on the server at build time, so I can’t call React state setters like setData or setLoading.
    • I also mistakenly mixed axios with response.json(), which only works with fetch.
  • App Router vs Pages Router confusion
    My stack trace pointed to src/app/page.js, meaning I was on the App Router. In that case, getStaticProps doesn’t even exist I should be using fetch in Server Components instead.

Fix the Build Error

The first thing I did was fix the follow-redirects error. Here are the commands I ran:

# Install the optional peer dependency
npm i debug

# Update axios
npm i axios@latest

# Clean and rebuild
rm -rf .next node_modules package-lock.json
npm i
npm run build

In most cases, installing debug clears the resolver problem. If you’re targeting the Edge Runtime, though, axios won’t work (it depends on Node APIs). In that case, I recommend switching to fetch.

Correct Data Fetching Pattern

The right fix depends on whether you’re in the Pages Router (pages/ folder) or the App Router (src/app/ folder).

Pages Router (with getStaticProps)

// pages/index.js
import axios from 'axios';

export async function getStaticProps() {
  const { data } = await axios.get('https://my-api-url.com/getlist');

  return {
    props: { initialData: data },
    revalidate: 3600, // re-build every hour
  };
}

export default function Home({ initialData }) {
  return (
    <main>
      <h1>Home</h1>
      <pre>{JSON.stringify(initialData, null, 2)}</pre>
    </main>
  );
}

✅ No setState calls.
✅ Return props.
✅ Use revalidate for ISR (Incremental Static Regeneration).

App Router (no getStaticProps support)

If you’re using the App Router, you should use Server Components with fetch.

Use fetch (recommended)

// src/app/page.js
export const revalidate = 3600;

export default async function Page() {
  const res = await fetch('https://my-api-url.com/getlist');
  
  if (!res.ok) {
    return <div>Failed to load data.</div>;
  }

  const data = await res.json();

  return (
    <main>
      <h1>Home</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </main>
  );
}

Stick with axios (Node.js runtime only)

// src/app/page.js
import axios from 'axios';
export const revalidate = 3600;

export default async function Page() {
  const { data } = await axios.get('https://my-api-url.com/getlist');
  return (
    <main>
      <h1>Home</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </main>
  );
}

Bonus Practice Enhancement

Once I got the basics working, I decided to add more robust functionality to my project.

Axios Helper with Retry + Timeout

// src/lib/http.js
import axios from 'axios';

export const http = axios.create({
  timeout: 10_000,
});

http.interceptors.response.use(
  (res) => res,
  async (error) => {
    const cfg = error.config;
    if (!cfg) throw error;

    cfg.__retryCount = cfg.__retryCount ?? 0;

    const status = error.response?.status;
    const shouldRetry =
      cfg.method?.toLowerCase() === 'get' &&
      cfg.__retryCount < 2 &&
      (!status || status >= 500);

    if (!shouldRetry) throw error;

    cfg.__retryCount++;
    await new Promise((r) => setTimeout(r, 500 * cfg.__retryCount));
    return http(cfg);
  }
);

Now, transient network issues don’t break my build.

Validate Responses with Zod

import { z } from 'zod';

const Item = z.object({
  id: z.number(),
  name: z.string(),
});
const ApiSchema = z.array(Item);

const data = ApiSchema.parse(await res.json());

This gave me confidence that my API returned what I expected.

Client Side Fallback with SWR

'use client';

import useSWR from 'swr';
import axios from 'axios';

const fetcher = (url) => axios.get(url).then((r) => r.data);

export default function ListClient() {
  const { data, error, isLoading, mutate } = useSWR(
    'https://my-api-url.com/getlist',
    fetcher,
    { revalidateOnFocus: false }
  );

  if (isLoading) return <p>Loading…</p>;
  if (error) return (
    <div>
      <p>Failed to load. {error.message}</p>
      <button onClick={() => mutate()}>Retry</button>
    </div>
  );

  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

With this, users get a retry button instead of a blank error screen.

Final Thought

Fixing the “Module not found” error in Next.js taught me that most issues aren’t just about missing packages they’re about understanding how the framework expects you to fetch data. By installing the missing dependency, using the right data-fetching strategy (Pages vs App Router), and adding small enhancements like retries and validation, I not only solved the error but also made my app more reliable and easier to maintain.

Related blog posts