vitest-dev/vitest · error · TypeError

invalid snapshot serializer in

Error message

invalid snapshot serializer in ${file}. Must have a 'test' method along with either a 'serialize' or 'print' method.

What it means

Thrown (as TypeError) by loadSnapshotSerializers after the default export is confirmed to be an object, if that object does not satisfy the SnapshotSerializer interface — specifically it must have a `test` function AND at least one of `serialize` or `print`. This mirrors Jest's serializer contract used by @vitest/snapshot's addSerializer.

Solutions

  1. Implement all required methods: a `test(val)` function plus either `serialize(val, config, indentation, depth, refs, printer)` or `print(val, printer)`.
  2. Use `serialize` for full custom formatting; use `print` only when delegating to the built-in printer.
  3. Confirm the exported object is the serializer itself, not settings for one.

Example fix

// before
export default { test: 'yes' }

// after
export default {
  test: v => v?.constructor?.name === 'Money',
  serialize: (val, config, indentation, depth, refs, printer) => {
    return printer(`$${val.amount}`, config, indentation, depth, refs)
  },
}
Defensive patterns

Strategy: type-guard

Validate before calling

function assertSerializer(v: unknown) {
  const o = v as any
  if (typeof o?.test !== 'function') throw new TypeError('serializer.test must be a function')
  if (typeof o?.serialize !== 'function' && typeof o?.print !== 'function') {
    throw new TypeError('serializer must have serialize or print')
  }
}

Type guard

type Serializer = { test: Function; serialize?: Function; print?: Function }
function isSerializer(v: unknown): v is Serializer {
  const o = v as any
  return typeof o?.test === 'function'
    && (typeof o?.serialize === 'function' || typeof o?.print === 'function')
}

Prevention

When it happens

Trigger: A snapshotSerializers entry default-exports an object that is missing `test`, missing both `serialize` and `print`, or where those keys are not functions (e.g. a plain config object was exported by mistake).

Common situations: Exporting a configuration object instead of a serializer; partially implementing the serializer interface; copy-paste errors that drop a method; version drift where a serializer was written against an older shape.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/9d9f4acbe6e666f6. Report an issue: GitHub.

Appendix: source

Thrown at packages/vitest/src/runtime/setup-common.ts:85

): Promise<void> {
  const files = config.snapshotSerializers

  const snapshotSerializers = await Promise.all(
    files.map(async (file) => {
      const mo = await moduleRunner.import(file)
      if (!mo || typeof mo.default !== 'object' || mo.default === null) {
        throw new Error(
          `invalid snapshot serializer file ${file}. Must export a default object`,
        )
      }

      const config = mo.default
      if (
        typeof config.test !== 'function'
        || (typeof config.serialize !== 'function'
          && typeof config.print !== 'function')
      ) {
        throw new TypeError(
          `invalid snapshot serializer in ${file}. Must have a 'test' method along with either a 'serialize' or 'print' method.`,
        )
      }

      return config as SnapshotSerializer
    }),
  )

  snapshotSerializers.forEach(serializer => addSerializer(serializer))
}

View on GitHub (pinned to 1fa9837ec2)