fastify/fastify · error · TypeError

FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT

FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT

Error message

'handlerTimeout' option must be an integer > 0. Got '%s'

What it means

Thrown by validateHandlerTimeoutOption (lib/route.js:631) when a route's handlerTimeout option is defined but is not a positive integer. handlerTimeout is the per-request wall-clock limit (milliseconds) after which Fastify aborts the request with FST_ERR_HANDLER_TIMEOUT (503). Strings, floats, zero, and negatives are rejected.

Source

Thrown at lib/route.js:631

}

function validateSchemaBodyOption (method, path, schema) {
  if (this[kSupportedHTTPMethods].bodyless.has(method) && schema?.body) {
    throw new FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED(method, path)
  }
}

function validateBodyLimitOption (bodyLimit) {
  if (bodyLimit === undefined) return
  if (!Number.isInteger(bodyLimit) || bodyLimit <= 0) {
    throw new FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT(bodyLimit)
  }
}

function validateHandlerTimeoutOption (handlerTimeout) {
  if (handlerTimeout === undefined) return
  if (!Number.isInteger(handlerTimeout) || handlerTimeout <= 0) {
    throw new FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT(handlerTimeout)
  }
}

function validateLogLevelOption (logLevel, method, path, logger) {
  if (logLevel == null || logLevel === '') return
  if (logger?.levels?.values == null) return

  if (typeof logLevel !== 'string' || logger.levels.values[logLevel] === undefined) {
    throw new FST_ERR_ROUTE_LOG_LEVEL_INVALID(method, path, logLevel)
  }
}

function runPreParsing (err, request, reply) {
  if (reply.sent === true) return
  if (err != null) {
    reply[kReplyIsError] = true
    reply.send(err)
    return

View on GitHub (pinned to 7299a57d3f)

Solutions

  1. Pass a positive integer in milliseconds: handlerTimeout: 5000.
  2. Coerce config strings: handlerTimeout: Number(opts.handlerTimeout) after an integer check.
  3. Omit the option to use the server-level handlerTimeout (default 0, meaning no limit).

Example fix

// before
fastify.get('/slow', { handlerTimeout: '5000' }, handler)

// after
fastify.get('/slow', { handlerTimeout: 5000 }, handler)
Defensive patterns

Strategy: validation

Validate before calling

function resolveHandlerTimeout (raw) {
  if (raw == null) return undefined
  const n = Number(raw)
  if (!Number.isInteger(n) || n <= 0) throw new TypeError(`handlerTimeout must be a positive integer ms, got ${raw}`)
  return n
}
routeOpts.handlerTimeout = resolveHandlerTimeout(config.timeout)

Type guard

const isPositiveIntMs = (v) => Number.isInteger(v) && v > 0

Prevention

When it happens

Trigger: fastify.get('/x', { handlerTimeout: '5000', handler }). handlerTimeout: 0, handlerTimeout: 2.5, or handlerTimeout loaded from config as a string.

Common situations: Reading the timeout from a JSON/YAML config or env var where it serializes as a string. Passing microseconds instead of milliseconds by mistake. Setting 0 expecting 'no timeout' (omit the option or use the server default 0 instead).

Related errors


AI-assisted analysis of fastify/fastify@7299a57d3f (2026-08-03). Data as JSON: /data/errors/c436c43243b257b6.json. Report an issue: GitHub.