immich-app/immich · error · Error

Schema validation failed

Error message

Schema validation failed:
  ${errors.join('
  ')}

What it means

patchOpenAPI post-processes the generated OpenAPI document and validates every component schema's numeric properties. Any number-typed property must declare an explicit format (e.g. 'double') and must not use the disallowed 'float' format; violations are accumulated and thrown together as a single Error from the swagger document build in useSwagger. This enforces a project-wide convention that all OpenAPI numbers are explicitly formatted, which some client generators and strict validators require.

Solutions

  1. Find the offending schema.property named in the error message (format 'SchemaName.propertyKey') and add .meta({ format: 'double' }) or an explicit format to its zod/number definition
  2. Replace any format: 'float' with format: 'double' (float is explicitly rejected by this validator)
  3. If the property should be an integer, change the zod schema from z.number() to z.number().int() so it generates type:'integer' instead of type:'number'
  4. Re-run the server; patchOpenAPI throws on the first validation pass, so iterate until the error list is empty

Example fix

// before
z.number().meta({ description: 'Score' })
// or
z.number()

// after
z.number().meta({ description: 'Score', format: 'double' })
// or, for integers:
z.number().int()
Defensive patterns

Strategy: validation

Validate before calling

// Audit all registered schemas before server start:
for (const [name, schema] of Object.entries(registry.definitions)) {
  for (const [key, prop] of Object.entries((schema as any).properties ?? {})) {
    const target = prop.type === 'array' ? prop.items : prop;
    if (target?.type === 'number') {
      if (!target.format) throw new Error(`${name}.${key} needs .meta({ format: 'double' })`);
      if (target.format === 'float') throw new Error(`${name}.${key}: use 'double' not 'float'`);
    }
  }
}

Type guard

function isSchema(v: unknown): v is SchemaObject { return typeof v === 'object' && v !== null && 'type' in v; }

Try / catch

try {
  useSwagger(app);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Schema validation failed:')) {
    console.error('Fix these OpenAPI schemas (see each listed property):\n' + err.message);
    process.exit(1); // fail fast at startup, do not serve broken docs
  }
  throw err;
}

Prevention

When it happens

Trigger: A route's response/request schema (defined via a zod-to-openapi registry or .meta() declarations) contains a property whose generated OpenAPI schema has type:'number' with either format:'float' or no format at all; the error fires when useSwagger calls patchOpenAPI on the document at startup.

Common situations: Declaring z.number() without .meta({ format: 'double' }) in an OpenAPI registry schema; using float format out of habit from other ecosystems; upgrading the framework so previously tolerated number schemas are now checked; adding a new endpoint whose DTO uses plain numbers.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of immich-app/immich@4c7b30c18b (2026-09-07). Data as JSON: /api/errors/aa524e8f1ffdc756. Report an issue: GitHub.

Appendix: source

Thrown at server/src/utils/misc.ts:264

        }

        if (isSchema(value) && value.type === 'number') {
          if (value.format === 'float') {
            errors.push(`Invalid number format: ${schemaName}.${key}=float (use double instead). `);
          }

          // verify it was meant to be a number (and not an integer)
          if (!value.format) {
            errors.push(
              `${schemaName}.${key} is a number (not an integer) and requires a format (e.g .meta({ format: 'double' })). `,
            );
          }
        }
      }
      schema.required?.sort();

      if (errors.length > 0) {
        throw new Error(`Schema validation failed:\n  ${errors.join('\n  ')}`);
      }
    }
  }

  for (const [key, value] of Object.entries(document.paths)) {
    const newKey = key.replace('/api/', '/');
    delete document.paths[key];
    document.paths[newKey] = value;
  }

  for (const path of Object.values(document.paths)) {
    const operations = {
      get: path.get,
      put: path.put,
      post: path.post,
      delete: path.delete,
      options: path.options,
      head: path.head,

View on GitHub (pinned to 4c7b30c18b)