jestjs/jest · error · TypeError

Jest: You can only define a single loader through…

Error message

Jest: You can only define a single loader through docblocks, got "${tsLoader.join(', ')}"

What it means

Thrown as a TypeError by loadTSConfigFile when the jest-docblock parser returns an Array for the @jest-config-loader pragma. Docblock pragmas become arrays when the same tag is declared more than once in the file header, and the loader supports only a single loader.

Solutions

  1. Open the config file and remove all but one @jest-config-loader line
  2. Choose the loader you actually want (ts-node or esbuild-register) and keep only that one
  3. Run prettier/eslint with docblock awareness to catch duplicate pragmas in future

Example fix

/**
 * @jest-config-loader ts-node
 * @jest-config-loader esbuild-register
 */
// before: two loaders -> error

/**
 * @jest-config-loader ts-node
 */
// after: single loader
Defensive patterns

Strategy: validation

Validate before calling

import { extract, parse } from 'jest-docblock';
import * as fs from 'node:fs';
const pragmas = parse(extract(fs.readFileSync('jest.config.ts', 'utf8')));
if (Array.isArray(pragmas['jest-config-loader'])) {
  throw new Error('Duplicate @jest-config-loader pragma detected');
}

Type guard

function hasSingleLoaderPragma(pragmas: Record<string, unknown>): boolean {
  const v = pragmas['jest-config-loader'];
  return v === undefined || typeof v === 'string';
}

Prevention

When it happens

Trigger: Having two or more '@jest-config-loader' lines in the jest.config.ts/.cts docblock header, e.g. one declaring ts-node and another esbuild-register.

Common situations: Merging config snippets from different sources without deduplicating the docblock; copying examples that each carried their own loader pragma; tooling that auto-injects pragmas.

Related errors


AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10). Data as JSON: /api/errors/443f73b6c86e855a. Report an issue: GitHub.

Appendix: source

Thrown at packages/jest-config/src/readConfigFileAndSetRootDir.ts:163

const loadDocblockPragmasInConfig = (configPath: string): Pragmas => {
  const docblockPragmas = parse(extract(fs.readFileSync(configPath, 'utf8')));
  return docblockPragmas;
};

const loadTSConfigFile = async (
  configPath: string,
): Promise<Config.InitialOptions> => {
  // Get registered TypeScript compiler instance
  const docblockPragmas = loadDocblockPragmasInConfig(configPath);
  const tsLoader = docblockPragmas['jest-config-loader'] || 'ts-node';
  const docblockTSLoaderOptions = docblockPragmas['jest-config-loader-options'];

  if (typeof docblockTSLoaderOptions === 'string') {
    extraTSLoaderOptions = JSON.parse(docblockTSLoaderOptions);
  }
  if (Array.isArray(tsLoader)) {
    throw new TypeError(
      `Jest: You can only define a single loader through docblocks, got "${tsLoader.join(
        ', ',
      )}"`,
    );
  }

  const registeredCompiler = await getRegisteredCompiler(
    tsLoader as TsLoaderModule,
  );
  registeredCompiler.enabled(true);

  let configObject = interopRequireDefault(require(configPath)).default;

  // In case the config is a function which imports more Typescript code
  if (typeof configObject === 'function') {
    configObject = await configObject();
  }

View on GitHub (pinned to 8e6d128e4a)