Every attempt to perform an API call via `fetch()` when deploying a project fails (even though it works locally)

Hey there,

I’m experiencing an issue with the fetch() function while trying to build a project (and would be appreciate any help).

The project npm build script has the following command: node <file.js>. This file has an API call to https://www.imf.org/external/np/fin/data/rms_mth.aspx via the fetch() function. When I run npm run build locally, everything works perfectly. However, when I try to deploy the project, fetch() fails, and I end up either with an ECONNRESET or ETIMEDOUT (in most cases) error.

The Netlify site name is tadataexchange .

Here is a screenshot of my build settings:

And the deploy log of my last attempt to build the project:

4:42:50 PM: Build ready to start
4:43:03 PM: build-image version: d52039e9772c55f8db143b008c4d01bc5305a9b4 (focal)
4:43:03 PM: buildbot version: d52039e9772c55f8db143b008c4d01bc5305a9b4
4:43:03 PM: Fetching cached dependencies
4:43:03 PM: Starting to download cache of 173.3MB
4:43:04 PM: Finished downloading cache in 1.444s
4:43:04 PM: Starting to extract cache
4:43:06 PM: Finished extracting cache in 1.903s
4:43:06 PM: Finished fetching cache in 3.391s
4:43:06 PM: Starting to prepare the repo for build
4:43:07 PM: Preparing Git Reference pull/5/head
4:43:09 PM: Starting to install dependencies
4:43:09 PM: Started restoring cached mise cache
4:43:09 PM: Finished restoring cached mise cache
4:43:10 PM: mise python@3.13.1   install
4:43:10 PM: mise python@3.13.1   download cpython-3.13.1+20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz
4:43:10 PM: mise python@3.13.1   extract cpython-3.13.1+20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz
4:43:10 PM: mise python@3.13.1   python --version
4:43:10 PM: mise python@3.13.1   Python 3.13.1
4:43:10 PM: mise python@3.13.1 ✓ installed
4:43:10 PM: Python version set to 3.13
4:43:12 PM: Collecting pipenv
4:43:12 PM:   Downloading pipenv-2024.4.0-py3-none-any.whl.metadata (19 kB)
4:43:12 PM: Collecting certifi (from pipenv)
4:43:12 PM:   Downloading certifi-2024.12.14-py3-none-any.whl.metadata (2.3 kB)
4:43:12 PM: Collecting packaging>=22 (from pipenv)
4:43:12 PM:   Downloading packaging-24.2-py3-none-any.whl.metadata (3.2 kB)
4:43:12 PM: Collecting setuptools>=67 (from pipenv)
4:43:12 PM:   Downloading setuptools-75.7.0-py3-none-any.whl.metadata (6.7 kB)
4:43:12 PM: Collecting virtualenv>=20.24.2 (from pipenv)
4:43:12 PM:   Downloading virtualenv-20.28.1-py3-none-any.whl.metadata (4.5 kB)
4:43:12 PM: Collecting distlib<1,>=0.3.7 (from virtualenv>=20.24.2->pipenv)
4:43:12 PM:   Downloading distlib-0.3.9-py2.py3-none-any.whl.metadata (5.2 kB)
4:43:12 PM: Collecting filelock<4,>=3.12.2 (from virtualenv>=20.24.2->pipenv)
4:43:12 PM:   Downloading filelock-3.16.1-py3-none-any.whl.metadata (2.9 kB)
4:43:12 PM: Collecting platformdirs<5,>=3.9.1 (from virtualenv>=20.24.2->pipenv)
4:43:12 PM:   Downloading platformdirs-4.3.6-py3-none-any.whl.metadata (11 kB)
4:43:12 PM: Downloading pipenv-2024.4.0-py3-none-any.whl (3.0 MB)
4:43:12 PM:    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.0/3.0 MB 70.5 MB/s eta 0:00:00
4:43:12 PM: Downloading packaging-24.2-py3-none-any.whl (65 kB)
4:43:12 PM: Downloading setuptools-75.7.0-py3-none-any.whl (1.2 MB)
4:43:12 PM:    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.2/1.2 MB 70.7 MB/s eta 0:00:00
4:43:12 PM: Downloading virtualenv-20.28.1-py3-none-any.whl (4.3 MB)
4:43:12 PM:    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.3/4.3 MB 158.1 MB/s eta 0:00:00
4:43:12 PM: Downloading certifi-2024.12.14-py3-none-any.whl (164 kB)
4:43:12 PM: Downloading distlib-0.3.9-py2.py3-none-any.whl (468 kB)
4:43:12 PM: Downloading filelock-3.16.1-py3-none-any.whl (16 kB)
4:43:12 PM: Downloading platformdirs-4.3.6-py3-none-any.whl (18 kB)
4:43:13 PM: Installing collected packages: distlib, setuptools, platformdirs, packaging, filelock, certifi, virtualenv, pipenv
4:43:15 PM: Successfully installed certifi-2024.12.14 distlib-0.3.9 filelock-3.16.1 packaging-24.2 pipenv-2024.4.0 platformdirs-4.3.6 setuptools-75.7.0 virtualenv-20.28.1
4:43:15 PM: Attempting Ruby version 2.7.2, read from environment
4:43:16 PM: Using Ruby version 2.7.2
4:43:17 PM: Started restoring cached go cache
4:43:17 PM: Finished restoring cached go cache
4:43:18 PM: go version go1.19.13 linux/amd64
4:43:19 PM: Using PHP version 8.0
4:43:20 PM: Started restoring cached Node.js version
4:43:22 PM: Finished restoring cached Node.js version
4:43:23 PM: Downloading and installing node v22.12.0...
4:43:23 PM: Downloading https://nodejs.org/dist/v22.12.0/node-v22.12.0-linux-x64.tar.xz...
4:43:23 PM: Computing checksum with sha256sum
4:43:23 PM: Checksums matched!
4:43:26 PM: Now using node v22.12.0 (npm v10.9.0)
4:43:26 PM: Enabling Node.js Corepack
4:43:27 PM: Started restoring cached build plugins
4:43:27 PM: Finished restoring cached build plugins
4:43:27 PM: Started restoring cached corepack dependencies
4:43:27 PM: Finished restoring cached corepack dependencies
4:43:27 PM: No npm workspaces detected
4:43:27 PM: Started restoring cached node modules
4:43:27 PM: Finished restoring cached node modules
4:43:27 PM: Installing npm packages using npm version 10.9.0
4:43:27 PM: added 4 packages, and audited 6 packages in 545ms
4:43:27 PM: found 0 vulnerabilities
4:43:27 PM: npm packages installed
4:43:28 PM: Successfully installed dependencies
4:43:28 PM: Starting build script
4:43:29 PM: Detected 0 framework(s)
4:43:29 PM: Section completed: initializing
4:43:31 PM: ​
4:43:31 PM: Netlify Build                                                 
4:43:31 PM: ────────────────────────────────────────────────────────────────
4:43:31 PM: ​
4:43:31 PM: ❯ Version
4:43:31 PM:   @netlify/build 29.58.1
4:43:31 PM: ​
4:43:31 PM: ❯ Flags
4:43:31 PM:   accountId: 58f336ecc4d9cc48f2fbc00e
4:43:31 PM:   baseRelDir: true
4:43:31 PM:   buildId: 677bf9fa43f06b0008ae2d88
4:43:31 PM:   deployId: 677bf9fa43f06b0008ae2d8a
4:43:31 PM: ​
4:43:31 PM: ❯ Current directory
4:43:31 PM:   /opt/build/repo
4:43:31 PM: ​
4:43:31 PM: ❯ Config file
4:43:31 PM:   /opt/build/repo/netlify.toml
4:43:31 PM: ​
4:43:31 PM: ❯ Context
4:43:31 PM:   deploy-preview
4:43:31 PM: ​
4:43:31 PM: Build command from Netlify app                                
4:43:31 PM: ────────────────────────────────────────────────────────────────
4:43:31 PM: ​
4:43:31 PM: $ npm run build
4:43:31 PM: > exchange@0.0.1 build
4:43:31 PM: > node build/months.js && node build/meta.js
4:43:31 PM: Downloading data for 2025-01
4:43:31 PM: file:///opt/build/repo/build/get-month.js:30
4:43:31 PM: 		throw new Error("Error while downloading file:" + e, {cause: e});
4:43:31 PM: 		      ^
4:43:31 PM: Error: Error while downloading file:TypeError: fetch failed
4:43:31 PM:     at getMonth (file:///opt/build/repo/build/get-month.js:30:9)
4:43:31 PM:     at async file:///opt/build/repo/build/months.js:35:13 {
4:43:31 PM:   [cause]: TypeError: fetch failed
4:43:31 PM:       at node:internal/deps/undici/undici:13484:13
4:43:31 PM:       at async read (file:///opt/build/repo/src/read.js:15:18)
4:43:31 PM:       at async getMonth (file:///opt/build/repo/build/get-month.js:27:10)
4:43:31 PM:       at async file:///opt/build/repo/build/months.js:35:13 {
4:43:31 PM:     [cause]: Error: read ECONNRESET
4:43:31 PM:         at TLSWrap.onStreamRead (node:internal/stream_base_commons:216:20) {
4:43:31 PM:       errno: -104,
4:43:31 PM:       code: 'ECONNRESET',
4:43:31 PM:       syscall: 'read'
4:43:31 PM:     }
4:43:31 PM:   }
4:43:31 PM: }
4:43:31 PM: Node.js v22.12.0
4:43:31 PM: ​
4:43:31 PM: "build.command" failed                                        
4:43:31 PM: ────────────────────────────────────────────────────────────────
4:43:31 PM: ​
4:43:31 PM:   Error message
4:43:31 PM:   Command failed with exit code 1: npm run build (https://ntl.fyi/exit-code-1)
4:43:31 PM: ​
4:43:31 PM:   Error location
4:43:31 PM:   In Build command from Netlify app:
4:43:31 PM:   npm run build
4:43:31 PM: ​
4:43:31 PM:   Resolved config
4:43:31 PM:   build:
4:43:31 PM:     command: npm run build
4:43:31 PM:     commandOrigin: ui
4:43:31 PM:     environment:
4:43:31 PM:       - REBUILD_URL
4:43:31 PM:       - REVIEW_ID
4:43:31 PM:     publish: /opt/build/repo
4:43:31 PM:     publishOrigin: ui
4:43:31 PM:   functions:
4:43:31 PM:     '*':
4:43:31 PM:       included_files:
4:43:31 PM:         - data/**/*.json
4:43:31 PM:       node_bundler: esbuild
4:43:31 PM:   functionsDirectory: /opt/build/repo/functions
4:43:31 PM:   headers:
4:43:31 PM:     - for: /*
      values:
        Access-Control-Allow-Origin: '*'
    - for: /favicon.ico
      values:
        Content-Type: image/svg+xml
4:43:31 PM: Build failed due to a user error: Build script returned non-zero exit code: 2
4:43:32 PM: Failed during stage 'building site': Build script returned non-zero exit code: 2 (https://ntl.fyi/exit-code-2)
4:43:32 PM: Failing build: Failed to build site
4:43:32 PM: Finished processing build request in 29.178s

Thank you!

Regards,
Dmitry

Hi, @DmitrySharabin. Looking at those logs, the reason for the failure is not explained. We do not have any additional logging or information about this failure on the Netlify side.

Either the build code logs enough information to debug or it does not. In this case, it does not.

As this is not code which Netlify controls, we do not have a way to get more information about the failure. I suggest adding additional logging around those API call to help debug the issue.

Hey, @luke. Thank you for having looked into it. I appreciate it. :pray:

It turned out that the issue was gone if I passed some additional headers with the request (so the API endpoint kept the connection alive). So, I ended up with the following code snippet:

// ...
let options = {};

// Add headers only when running on Netlify (e.g., on deployment)
if (IS_NODE && process?.env?.NETLIFY) {
	options.headers = {
		"User-Agent": "Mozilla/5.0 (compatible; ExchangeRatesBot/1.0)",
		"Connection": "keep-alive"
	};
}

let response = await fetch(url, options);
// ...

Knowing this might be helpful for other folks trying to perform some API requests during the build process.

Regards,
Dmitry