# yarn network timeout ci: two Yarns, two different fixes

> A yarn network timeout ci failure reads differently in Yarn 1 and Yarn 4. Identify which one you are on, then use the setting that version has.

Source: https://latchkey.dev/learn/failures/yarn-network-timeout-in-ci  
Updated: 2026-09-20

A yarn network timeout ci failure comes from one of two completely different programs that happen to share a name, and the first job is to work out which one printed your log. Yarn 1 prints friendly sentences and a stack trace, Yarn 4 prints numbered codes and an indented field list, and almost every setting people recommend for one of them does not exist in the other.

## What this error means

Yarn 1 announces the trouble before it fails, repeating a line about your network connection once per retry, and then ends with an `error Error:` line carrying the URL and a code such as ETIMEDOUT. Yarn 4 prints nothing until it has finished retrying, then emits a YN0035 block with the response code, the method, the URL and the retry count it used. The two blocks below were produced in containers running yarn 1.22.22 and yarn 4.18.0 against servers we control: an unroutable address for the timeout, and a local 503 responder for the refusal. This page carries no reproduction on a Latchkey runner, so the hosts are ours.

```yarn 1.22.22, registry at an unroutable address
info There appears to be trouble with your network connection. Retrying...
[three more of the same notice, elided]
error Error: https://192.0.2.1/is-odd: ETIMEDOUT
```

## Common causes

### The registry or the path to it stalled

A socket that connects and then goes quiet, a proxy that accepts and does not forward, or a registry under enough load that it stops answering. Yarn 1 reports this as ETIMEDOUT with the URL; Yarn 4 reports a request that ran out of time rather than a response code, which is the same distinction other package managers make.

### One job is making far more requests than the registry expects

Yarn 4 sets `networkConcurrency` to 50 by default, and a matrix multiplies that across jobs. An internal registry that is comfortable with a developer laptop can start dropping connections when a CI fleet arrives, and the result looks like a timeout rather than like throttling.

### Nothing is cached, so every job re-fetches the whole tree

An install with a cold cache makes one request per package. That is the exposure, and it is entirely avoidable. Yarn 4 in particular is designed around a checked in or cached offline mirror, and the YN0035 message says so directly by noting that a local cache might have protected you.

### A migration left Yarn 1 settings in a Yarn 4 repository

A `--network-timeout` flag in a workflow, or a `.yarnrc` that Yarn 4 does not read, quietly does nothing. In our experience this is why a team reports that raising the timeout did not help: the value never reached the program that was timing out.

## How to fix it

### On Yarn 4, set httpRetry and httpTimeout in .yarnrc.yml

1. Put both settings in the repository so every machine and every runner agrees, rather than passing flags in one workflow.
2. Raise `httpRetry` before you raise `httpTimeout`. A registry that is briefly unavailable needs more attempts, not one longer attempt.
3. Confirm the change took effect by reading the retry count in the next YN0035 block, which prints the number it used.

```.yarnrc.yml
httpRetry: 5
httpTimeout: 120000
networkConcurrency: 16
```

### On Yarn 1, raise the network timeout and accept the fixed retries

The two network levers Yarn 1 exposes on the command line are the timeout and the concurrency. `NETWORK_TIMEOUT` in its constants is 30 seconds and `--network-timeout` overrides it, which helps a slow link and does nothing for an unreachable host. `NETWORK_CONCURRENCY` is 8, so passing `--network-concurrency 8` changes nothing; go below it or leave it out. The retry count is not configurable at all, so if five attempts are not enough the answer is to make fewer requests.

```Terminal
yarn install --frozen-lockfile --network-timeout 120000 --network-concurrency 4
```

### Cache the right directory for your version

The two versions keep their caches in different places, which is another thing a migration gets wrong. Cache the one your version actually uses, keyed on the lockfile, and most installs stop touching the registry at all.

```.github/workflows/ci.yml
- uses: actions/cache@v4
  with:
    path: .yarn/cache
    key: yarn4-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
```

### Put the registry behind TLS before moving to Yarn 4

Yarn 4 declines plain HTTP registries by default, and the whitelist exists for situations you cannot change rather than as a normal configuration. Doing the TLS work as part of the migration avoids a failure that arrives on the first install and looks like a network problem.

```.yarnrc.yml
unsafeHttpWhitelist:
  - registry.internal.example.com
```

## How to prevent it

- Decide which Yarn the repository is on and delete the settings that belong to the other one.
- Cache `.yarn/cache` on Yarn 4 or the Yarn 1 cache directory on Yarn 1, keyed on the lockfile.
- Set `networkConcurrency` for CI when the registry is one you operate.
- Move internal registries behind TLS rather than whitelisting plain HTTP and forgetting about it.

## Tell the two apart in one line of log

If the log has lines beginning with `info`, `warning` and `error` in lower case, and ends in a Node stack trace, it is Yarn 1. If every line opens with an arrow glyph and a code like YN0000 or YN0035, it is Yarn 2 or later, which in practice means Yarn 4. Everything after this point depends on that answer.

The Yarn 4 form for the same class of failure is a block rather than a sentence, and it is considerably more useful because it tells you how many retries it performed. Against a registry answering 503, with default settings, it reported three.

```yarn 4.18.0, registry answering 503
➤ YN0035: │ is-odd@npm:3.0.1: The registry appears to be down (using a local cache might have protected you against such outages)
➤ YN0035: │   Response Code: 503 (Service Unavailable)
➤ YN0035: │   Request Method: GET
➤ YN0035: │   Request URL: http://mirror503:8899/is-odd
➤ YN0035: │   Request Retry Count: 3 (can be increased via httpRetry)
```

|  | Yarn 1.22.22 | Yarn 4.18.0 |
| --- | --- | --- |
| Message on failure | `error Error: <url>: ETIMEDOUT` | A YN0035 block with the response code |
| Attempts before giving up | Five, measured against a 503 responder | Four, measured: one try plus three retries |
| Timeout setting | `--network-timeout`, in milliseconds, default 30 seconds | `httpTimeout`, default `1m` |
| Retry setting | Not exposed on the command line | `httpRetry`, default 3 |
| Concurrency default | 8 | 50 |
| Plain HTTP registry | Allowed | Refused unless the host is whitelisted |

> Yarn 1 attempts five times in total because `maxRetryAttempts` is set to 5 in its request manager and nothing on the command line reaches it. Its `NETWORK_TIMEOUT` constant is 30 seconds unless `--network-timeout` overrides it.

## What each version actually retried

Counting requests at the server settles what the logs only imply. Yarn 4.18.0 with default settings sent four requests for the package before failing, and reported a retry count of three, which matches its documented `httpRetry` default. Setting `httpRetry` to zero produced exactly one request, and the block then read `Request Retry Count: 0`, so the field is reporting the setting rather than a fixed string.

Yarn 1.22.22 sent five requests to the same server and printed four of its retry notices along the way. That matches `maxRetryAttempts = 5` in its request manager rather than any setting, and `yarn --help` offers `--network-concurrency` and `--network-timeout` but nothing for retries, so the number really is out of reach from the command line.

Yarn 1 also picks its sentence by what went wrong, and the wording is diagnostic. Against an unroutable address it repeats "There appears to be trouble with your network connection." Against the 503 responder it repeats a different sentence, one that names the npm registry rather than your connection and carries a parenthetical for the status code that Yarn 1 leaves unfilled, and the run ends in a `ResponseError: Request failed "503 Service Unavailable"` rather than a socket error. If your Yarn 1 log names the registry rather than your connection, the registry answered.

This is the practical difference between the two. On Yarn 4 the retry budget is yours to set. On Yarn 1 it is not, so the only levers you have are the timeout, the concurrency and whether the request has to happen at all.

## Yarn 4 refuses plain HTTP, which reads like a network failure

One failure that gets filed under network timeouts is not one. Pointing Yarn 4 at an `http://` registry without whitelisting the host produced `➤ YN0081: │ is-odd@npm:3.0.1: Unsafe http requests must be explicitly whitelisted in your configuration (mirror503)` and stopped. No request reached the server, so nothing timed out, and the whole run finished in sixteen milliseconds, but the job failed at the same point in the install and the log looks similar at a glance.

If you are moving an internal registry from Yarn 1 to Yarn 4, this will be the first thing you hit. Either put the registry behind TLS, which is the right answer, or add the host to the whitelist in `.yarnrc.yml` deliberately.

```.yarnrc.yml
npmRegistryServer: "https://registry.internal.example.com"
httpRetry: 5
httpTimeout: 120000
```

## Why this page has no runner reproduction

Both failures need a registry that misbehaves on demand. An unroutable address gives a genuine socket timeout and a server answering 503 gives a genuine refusal, and both run in a container in seconds. A runner job would have produced the same two messages with a different hostname in them, which is not more evidence, and it would have cost minutes to get.

Counting the requests each Yarn sent is the part that mattered most here, and that needs a server you own on the other end. There is no entry for this slug in `content/heal-evidence.mjs`, so this page is not marked `healable` and makes no claim that Latchkey repairs it.

## FAQ

### How many times does Yarn retry a failed registry request?

It depends on the Yarn. Yarn 4 retries three times by default, which is the `httpRetry` setting, and prints the count it used in the YN0035 block; we counted four requests reaching the server, and one request after setting `httpRetry` to zero. Yarn 1 attempts five times in total, which we also counted, and does not expose that number as a flag, because `maxRetryAttempts` is fixed in its request manager.

### Does --network-timeout work in Yarn 4?

No. That flag belongs to Yarn 1. On Yarn 4 the equivalent is `httpTimeout`, which defaults to one minute and is set in `.yarnrc.yml`. A workflow that still passes the old flag after a migration is changing nothing, which is a common reason a timeout increase appears to have no effect.

### What does YN0035 mean in a Yarn install?

YN0035 is Yarn 4's network error code. The block under it carries the response code, the request method, the URL and the retry count that was used. When the status is in the 500 range Yarn adds a sentence saying the registry appears to be down and pointing out that a local cache would have protected the install.

### Why does Yarn 4 refuse my internal registry over http?

Because it treats plain HTTP requests as unsafe unless the host is whitelisted, and it fails before any request goes out. The message names the host and says the whitelist is required. Serve the registry over TLS if you can, and add the host to `unsafeHttpWhitelist` only when you genuinely cannot.

## References

- [Yarn configuration reference: httpRetry, httpTimeout and networkConcurrency](https://yarnpkg.com/configuration/yarnrc)
- [yarnpkg/berry: httpUtils.ts, where the YN0035 block is assembled](https://github.com/yarnpkg/berry/blob/%40yarnpkg/cli/4.18.0/packages/yarnpkg-core/sources/httpUtils.ts)
- [yarnpkg/yarn: constants.js, the 30 second NETWORK_TIMEOUT in Yarn 1](https://github.com/yarnpkg/yarn/blob/v1.22.22/src/constants.js)
- [yarnpkg/yarn: request-manager.js, the fixed five attempt maximum](https://github.com/yarnpkg/yarn/blob/v1.22.22/src/util/request-manager.js)

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
