fastify/fastify · error · Error

FST_ERR_SCH_DUPLICATE

FST_ERR_SCH_DUPLICATE

Error message

Schema with '%s' already present!

What it means

Thrown by normalizeSchema (lib/schemas.js:67) when a route schema defines both query and querystring keys. Fastify treats query as an alias for querystring and copies it over; defining both is ambiguous, so it is rejected to prevent silent shadowing.

Source

Thrown at lib/schemas.js:67

 * Checks whether a schema is a non-plain object.
 *
 * @param {*} schema the schema to check
 * @returns {boolean} true if schema has a custom prototype
 */
function isCustomSchemaPrototype (schema) {
  return typeof schema === 'object' && Object.getPrototypeOf(schema) !== Object.prototype
}

function normalizeSchema (routeSchemas, serverOptions) {
  if (routeSchemas[kSchemaVisited]) {
    return routeSchemas
  }

  // alias query to querystring schema
  if (routeSchemas.query) {
    // check if our schema has both querystring and query
    if (routeSchemas.querystring) {
      throw new FST_ERR_SCH_DUPLICATE('querystring')
    }
    routeSchemas.querystring = routeSchemas.query
  }

  generateFluentSchema(routeSchemas)

  for (const key of SCHEMAS_SOURCE) {
    const schema = routeSchemas[key]
    if (schema && !isCustomSchemaPrototype(schema)) {
      if (key === 'body' && schema.content) {
        const contentProperty = schema.content
        const keys = Object.keys(contentProperty)
        for (let i = 0; i < keys.length; i++) {
          const contentType = keys[i]
          const contentSchema = contentProperty[contentType].schema
          if (!contentSchema) {
            throw new FST_ERR_SCH_CONTENT_MISSING_SCHEMA(contentType)
          }

View on GitHub (pinned to 7299a57d3f)

Solutions

  1. Keep only one of query or querystring in each route schema (query is the preferred short alias).
  2. If merging schema fragments programmatically, delete the duplicate key before passing to Fastify.
  3. Standardise your codebase on a single key name.

Example fix

// before
fastify.get('/x', { schema: { query: { type: 'object' }, querystring: { type: 'object' } } }, handler)

// after
fastify.get('/x', { schema: { querystring: { type: 'object' } } }, handler)
Defensive patterns

Strategy: validation

Validate before calling

function normalizeQueryAlias (schema) {
  if (schema && schema.query && schema.querystring) {
    throw new Error('Define only one of schema.query or schema.querystring')
  }
}

Prevention

When it happens

Trigger: fastify.get('/x', { schema: { query: { ... }, querystring: { ... } } }, handler). Copy-pasting between Fastify versions (older docs used querystring, examples now show query) and ending up with both.

Common situations: Migrating from older Fastify samples. Combining schemas from different team members where one used query and the other querystring. Generic schema factories that merge objects and accidentally produce both keys.

Related errors


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