---
title: 'Error Codes'
icon: 'triangle-exclamation'
---

The library runs a number of validation checks to look for common configuration
issues. If a validation check fails, an error is logged to the console.

Note that validation checks may be disabled globally by passing
`validate: false` in the options, or disabled individually with the settings
documented below. Additionally, specific checks will be disabled in some
circumstances, also discussed below. Otherwise, each check will run once then
disable itself.

<Note>

The ability to disable individual checks was added in version `7.0.0`.

</Note>

## Errors

### `ERR_ERL_UNDEFINED_IP_ADDRESS`

> Added in `6.8.0`.

This error is logged whenever `request.ip` is undefined. It could indicate a
misconfiguration in server settings, or that
[the client disconnected](https://nodejs.org/api/net.html#net_socket_remoteaddress)
before the request could be processed.

It could also indicate that a server other than [Express](http://expressjs.com/)
is being used, which is not a supported use-case.

This check will be prevented if a custom `keyGenerator` is supplied.

Set `validate: {ip: false}` in the options to disable the check.

### `ERR_ERL_INVALID_IP_ADDRESS`

> Added in `6.8.0`.

This error is logged whenever the IP address is not a valid IPV4/IPV6 address,
or when the IP address contains a port number. The port from which a request
comes can be changed simply by opening or closing a browser, or when using an
Azure proxy, thus opening up avenues for bypassing the rate limit.

Consider using a custom `keyGenerator` function that strips out the port number
(`request.ip.replace(/:\d+[^:]*$/, '')`, or that uses something like an API key
or bearer token to count the hits against.

See [#234](https://github.com/express-rate-limit/express-rate-limit/issues/234)
for more information on this issue.

This check will be prevented if a custom `keyGenerator` is supplied.

Set `validate: {ip: false}` in the options to disable the check.

### `ERR_ERL_PERMISSIVE_TRUST_PROXY`

> Added in `6.8.0`.

This error is logged when the `trust proxy` setting is set to `true`.

If this is set to true, it will cause express to return the leftmost entry in
the `X-Forwarded-For` header as the client's IP. This header could be set by the
proxy or a malicious client, opening up avenues for bypassing the rate limiter.

Refer to the
[troubleshooting proxy issues](/guides/troubleshooting-proxy-issues) page for a
guide to set the `trust proxy` value correctly.

This check will be prevented if a custom `keyGenerator` is supplied.

Set `validate: {trustProxy: false}` in the options to disable the check.

### `ERR_ERL_UNEXPECTED_X_FORWARDED_FOR`

> Added in `6.8.0`.

This error is logged when the `X-Forwarded-For` header is set (indicating use of
a proxy), but the `trust proxy` setting is `false` (which is the default value).

This usually indicates a configuration issue that will cause express-rate-limit
to apply it's limits global rather than on a per-user basis. Refer to the
[troubleshooting proxy issues](/guides/troubleshooting-proxy-issues) page for a
guide to set the `trust proxy` value correctly.

If this error occurs only rarely, and you do not have a reverse proxy, it may
indicate a malicious user probing for vulnerabilities.

This check will be prevented if a custom `keyGenerator` is supplied.

Set `validate: {xForwardedForHeader: false}` in the options to disable the
check.

### `ERR_ERL_FORWARDED_HEADER`

> Added in `8.1.0`.

This error is logged when the `Forwarded` header
([RFC 7239](https://tools.ietf.org/html/rfc7239)) is set (indicating use of a
proxy) and the default [`keyGenerator`] is used.

As of express@5.1.0, express's `trust proxy` setting does not support the
`Forwarded` header, only `X-Forwarded-For`, and express-rate-limit relies on
express to determine the requester's IP address, so this header is ignored in
the default configuration.

(There is an
[open ticket requesting support be added to the proxy-addr library](https://github.com/jshttp/proxy-addr/issues/20)
that express uses to determine IP address.)

To use this header, add a custom [keyGenerator] that parses it and returns the
correct IP. For example, using the
[forwarded-parse](https://www.npmjs.com/package/forwarded-parse) library:

```js
import parseForwarded from 'forwarded-parse'
import { rateLimit, ipKeyGenerator } from 'express-rate-limit'

// ...

const NUMBER_OF_PROXIES_TO_TRUST = 1

app.use(
	rateLimit({
		keyGenerator: (req, res) => {
			let ip = req.ip
			try {
				const forwards = parseForwarded(req.headers.forwarded)
				ip = forwards[forwards.length - NUMBER_OF_PROXIES_TO_TRUST].for
			} catch (ex) {
				console.error(
					`Error parsing Forwarded header ${req.headers.forwarded} from ${req.ip}:`,
					ex,
				)
			}
			return ipKeyGenerator(ip)
		},
		// ...
	}),
)
```

(`ipKeyGenerator` is needed for proper limiting of IPv6 users, see
[ERR_ERL_KEY_GEN_IPV6](#err-erl-ipv6subnet-or-keygenerator) for more info.)

If there are multiple proxies between your server and the internet and each adds
a `Forwarded` header, increment `NUMBER_OF_PROXIES_TO_TRUST` to match.

See [Troubleshooting Proxy Issues](/guides/troubleshooting-proxy-issues) for
additional information.

`req.ip` could also be set before express-rate-limit processes the request. If
it is set to anything other than the default value (`req.socket.remoteAddress`),
this error will be automatically suppressed.

Set `validate: {forwardedHeader: false}` in the options to disable the check.

### `ERR_ERL_INVALID_HITS`

> Added in `6.10.0`.

This indicates an issue with the Store and/or it's underlying data storage
mechanism.

Ensure the Store returns a positive integer for the `totalHits` value when
`increment()` is called. If this is not possible, the store should throw an
error.

Set `validate: {positiveHits: false}` in the options to disable the check.

### `ERR_ERL_STORE_REUSE`

> Added in `7.3.0`.

This indicates that the single [Store](./stores) instance was used in more than
one rate limiter. This can lead to problems such as initialization logic running
multiple times, and inconsistent reset times.

Instead, create a new store instance for each rate limiter. (Generally with a
unique `prefix` value for each one.)

Set `validate: {unsharedStore: false}` in the options to disable the check.

### `ERR_ERL_DOUBLE_COUNT`

> Added in `6.9.0`.

This indicates that the hit count for a given IP or key was incremented more
than once for a single request. It could happen if the same instance of
express-rate-limit is called more than once, or if multiple instances are called
that use the same [Store](/reference/stores).

- If only a single rate limit is desired, find and remove the extra rateLimit
  call(s).

- If multiple rate limits are desired, consider one of the following options:
  - Set a custom `prefix` in the configuration for each store instance.
    ([Redis](https://www.npmjs.com/package/rate-limit-redis#prefix),
    [Memcached](https://www.npmjs.com/package/rate-limit-memcached#prefix),
    [PostgreSQL](https://www.npmjs.com/package/@acpr/rate-limit-postgresql#constructor),
    [Cluster](https://www.npmjs.com/package/@express-rate-limit/cluster-memory-store#prefix))
  - Set a custom [`keyGenerator`](/reference/configuration#keygenerator) in the
    configuration for each express-rate-limit instance.

In rare circumstances this error can be a false positive. This would include
situations where multiple rate limiters are configured to use the same key but
different backed databases. Setting a unique key per rate limiter would prevent
this.

<Warning>

Prior to version `6.11.1`, the library did not take a store prefixes into
account, and thus could log this incorrectly if two instances use different
prefixes.

</Warning>

Set `validate: {singleCount: false}` in the options to disable the check.

### `ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION`

> Added in `7.5.0`

The library only implements the
[sixth](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-06),
[seventh](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-07)
and
[eighth](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-08)
drafts of the IETF RateLimit headers specification. If draft version other than
`draft-6`, `draft-7`, or `draft-8` is passed to the `standardHeaders` option,
this error is raised.

### `ERR_ERL_HEADERS_NO_RESET`

> Added in `7.0.0`.

The
[seventh draft](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-07)
of the IETF RateLimit headers specification makes `reset` a required field.
However, the legacy `Store` API did not have a way for stores to provide this
information.

The library attempts to account for this by using the `windowMs` time, which has
a default value of 60 seconds if not explicitly set.

To resolve this, either change to a different headers version, such as
`draft-6`, or a different [store](/reference/stores).

Set `validate: {headersResetTime: false}` in the options to disable the check.

### `ERR_ERL_UNKNOWN_OPTION`

> Added in `8.2.0`

Indicates that one or more of the configuration options passed to
express-rate-limit is unrecognized.

This could be due to a typo or deprecated option, or if extra values were
intentionally added to the configuration object.

See [Configuration](/reference/configuration) for the complete list of valid
configuration options.

Set `validate: {knownOptions: false}` in the options to disable the check.

### `ERR_ERL_UNKNOWN_VALIDATION`

> Added in `7.0.0`.

The library allows for specific validation checks to be enabled or disabled, but
only ones that it knows about. If an unknown validation check is referenced in
the `validate` configuration object, this error will be logged.

This could be due to a typo in the `validate` configuration, or the
configuration could be intended for a different version of the library that
includes the unrecognized validation check.

To resolve this, remove the specified key from your configuration.

To prevent the validation check that logs this error, set the
`validate.validationsConfig` option to `false`:

```js
const limiter = rateLimit({
	validate: {
		validationsConfig: false,
		// ...
		default: true,
	},
	// ...
})
```

Set `validate: {validationsConfig: false}` in the options to disable the check.

### `ERR_ERL_CREATED_IN_REQUEST_HANDLER`

Instances of express-rate-limit should be created before a request comes in, not
in response to one.

Incorrect example:

```js
import { rateLimit } from 'express-rate-limit'

app.use((req, res, next) => {
	// This won't work!
	rateLimit({/*...*/})
	next()
})
```

Correct example:

```js
import { rateLimit } from 'express-rate-limit'

app.use(rateLimit({/*...*/});
```

To only apply the rate limit to some requests, use the
[`skip`](/reference/configuration#skip),
[`skipSuccessfulRequests`](/reference/configuration#skipsuccessfulrequests), or
[`skipFailedRequests`](/reference/configuration#skipfailedrequests) options.

Example where the rate limit only applies to users who are not logged in:

```js
// first determine the user's logged-in status
app.use((req, res, next) => {
	if (/* user is logged in */) {
		req.isLoggedIn = true
	} else {
		req.isLoggedIn = false
	}
	next()
})

// next configure express-rate-limit to skip logged in users
app.use(rateLimit({
	skip: (req, res) => req.isLoggedIn,
	// ...
}))
```

If using a factory function to create express-rate-limit instances, ensure that
it is called at app initialization, not in response to each request:

```diff
  function rateLimitFactory() {
  	return rateLimit({/*...*/});
  }

- app.use(rateLimitFactory); // broken
+ app.use(rateLimitFactory()); // works
```

In certain advanced use cases, such as serving multiple websites from a single
app, it may be necessary to dynamically create a rate limiter in response to a
request. In this case the validation check may be disabled, but care should be
taken to ensure everything works as expected. Consider caching the limiter after
it's first usage to avoid repeating the initialization cost and any background
work the store may require.

Set `validate: {creationStack: false}` in the options to disable the check.

### `ERR_ERL_IPV6_SUBNET`

> Added in `8.0.0`.

The [`ipv6Subnet`](./configuration#ipv6subnet) option must be set to an integer
in the range of 1 to 128 (inclusive), or a function that returns such an
integer. Set it to `false` to disable the subnet masking.

_However_ this validation check is more restrictive in that it only allows for a
number in the more common range of 32 to 64. Subnet values less than /32 will
block a large portion of the internet, and on the other side, ISPs very rarely
give out subnets larger than /64 because certain IPv6 features (SLAAC) depend on
it. Disable this check if you are confident that larger or smaller subnets are
correct for your use-case.

Set `validate: {ipv6Subnet: false}` in the options to disable the check.

### `ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR`

> Added in `8.0.0`.

The [`ipv6Subnet`](./configuration#ipv6subnet) and
[`keyGenerator`](./configuration#keygenerator) options are mutually exclusive. A
custom `keyGenerator` function does respect the value of the `ipv6Subnet`
option - only the default `keyGenerator` makes use of it.

When using a custom `keyGenerator`, the `ipv6Subnet` should instead be set or
calculated inside the `keyGenerator` and passed as a second parameter to the
[`ipKeyGenerator`](./helpers#ipkeygenerator) helper function like so:

```js
import { rateLimit, ipKeyGenerator } from 'express-rate-limit'

const limiter = rateLimit({
	// ...
	keyGenerator: (req, res) => {
		if (req.query.apiKey) return req.query.apiKey

		const ipv6Subnet = 64 // calculate or set a fixed value here
		return ipKeyGenerator(req.ip, ipv6Subnet)
	},
})
```

Set `validate: {ipv6SubnetOrKeyGenerator: false}` in the options to disable the
check.

### `ERR_ERL_KEY_GEN_IPV6`

> Added in `8.0.0`.

This error indicates that your custom
[`keyGenerator`](./configuration#keygenerator) appears to have a vulnerability
where IPv6 users can easily bypass rate-limiting by rotating through their
available IP addresses. (See [`ipv6Subnet`](./configuration#ipv6subnet) setting
for more info.)

Correct this error by wrapping your use of `req.ip` with the
[`ipKeyGenerator`](./helpers#ipkeygenerator) helper function like so:

```js
 import { rateLimit, ipKeyGenerator } from 'express-rate-limit

 const limiter = rateLimit({
 	// ...
 	keyGenerator: (req, res) => {
 		// Use API key (or some other identifier) for authenticated users
 		if (req.query.apiKey) return req.query.apiKey

 		// fallback to IP for unauthenticated users
		// return req.ip // vulnerable
		return ipKeyGenerator(req.ip) // better
	}
 })
```

(See the [example above](#err-erl-ipv6subnet-or-keygenerator) for a custom
`ipv6Subnet`.)

Disable this check if your `keyGenerator` already takes IPv6 subnets into
account.

Set `validate: {keyGeneratorIpFallback: false}` in the options to disable the
check.

### `ERR_ERL_WINDOW_MS`

> Added in `8.1.0`

Node.js (as of version 24.6.0)
[limits `setInterval()` delays to the max 32-bit signed integer value of `2147483647` milliseconds](https://nodejs.org/api/timers.html#setintervalcallback-delay-args),
which is about 28.4 days. Accordingly, express-rate-limit checks the `windowMs`
value before using it in the default MemoryStore and warns when it's out of
range.

Longer values can be used with most external [stores](/reference/stores).

Set `validate: {windowMs: false}` in the options to disable the check.

## Warnings

### `WRN_ERL_MAX_ZERO`

> Added in `6.10.0`.

In express-rate-limit version 6 and older, the rate limiter would be disabled
when setting `limit` (previously `max` until version `7.0.0`) to `0` in the
options. Starting with version `7.0.0`, this is no longer the case - the rate
limiting will apply from the very first request instead. See
[#369](https://github.com/express-rate-limit/express-rate-limit/discussions/369)
for more information.

To recreate the original behavior of disabling the rate limiter entirely, use
the [skip](https://github.com/express-rate-limit/express-rate-limit#skip)
function instead.

Set `validate: {limit: false}` in the options to disable the check.

### `WRN_ERL_DEPRECATED_ON_LIMIT_REACHED`

> Added in `6.10.0`.

The `onLimitReached` configuration option was deprecated in express-rate-limit
v6 and removed in version `7.0.0`. To replicate it's behavior, set a custom
handler like so:

```js
const limiter = rateLimit({
	// ...
	handler: (request, response, next, options) => {
		if (request.rateLimit.used === request.rateLimit.limit + 1) {
			// onLimitReached code here
		}
		response.status(options.statusCode).send(options.message)
	},
})
```

([Alternatives for `express-slow-down` users](https://github.com/express-rate-limit/express-slow-down/issues/45#issuecomment-1813098848))

Set `validate: {onLimitReached: false}` in the options to disable the check.

### `WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS`

> Added in `6.10.0`.

The `draft_polli_ratelimit_headers` option was deprecated in express-rate-limit
v6 and removed in version `7.0.0`. Please use the `standardHeaders: 'draft-6'`
option to replicate its behaviour, or use the recommended
`standardHeaders: 'draft-7'` option instead.

Set `validate: {draftPolliHeaders: false}` in the options to disable the check.

[`keyGenerator`]: /reference/configuration#keygenerator
[keyGenerator]: /reference/configuration#keygenerator
