jestjs/jest · error · Error

Table headings do not conform to expected format: heading1…

Error message

Table headings do not conform to expected format:

heading1 | headingN

Received:

${pretty(headings)}

What it means

Thrown by `extractValidTemplateHeadings` when the heading string does not match the expected pipe-delimited format regex (`^\n\s*\S+\s*(\|\s*\S+\s*)*\n`). The heading row must be space-delimited tokens separated by pipes with newlines around them.

Solutions

  1. Format headings as pipe-separated: `` `a | b | c` `` with a newline before and after.
  2. Ensure each heading is a non-whitespace token separated by ` | `.
  3. Switch to array-of-arrays syntax if the template heading format is too finicky.

Example fix

// before — commas instead of pipes
test.each`a, b
  ${1} | ${2}`('test', ({a,b}) => {});

// after — pipe-separated
test.each`a | b
  ${1} | ${2}`('test', ({a,b}) => {});
Defensive patterns

Strategy: validation

Validate before calling

// Validate heading format matches expected pipe-delimited pattern
const HEADING_RE = /^\s*\S+\s*(\|\s*\S+\s*)*$/;
function validateHeadingFormat(headings) {
  if (!HEADING_RE.test(headings.trim())) {
    throw new Error(`Headings must be pipe-separated: 'a | b | c', got: ${headings}`);
  }
}

Prevention

When it happens

Trigger: Using commas instead of pipes (`` test.each`a, b\n ${1} ${2}` ``), using a single heading with no pipe when multiple columns are needed, or having no surrounding newlines in the template structure. The regex match returns null.

Common situations: Migrating from comma-separated to pipe-separated format and forgetting to update the header. Using tab or semicolon delimiters. Missing the newline after the header row in the template literal.

Related errors


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

Appendix: source

Thrown at packages/jest-each/src/validation.ts:90

};

const pluralize = (word: string, count: number) =>
  word + (count === 1 ? '' : 's');

const START_OF_LINE = '^';
const NEWLINE = '\\n';
const HEADING = '\\s*[^\\s]+\\s*';
const PIPE = '\\|';
const REPEATABLE_HEADING = `(${PIPE}${HEADING})*`;
const HEADINGS_FORMAT = new RegExp(
  START_OF_LINE + NEWLINE + HEADING + REPEATABLE_HEADING + NEWLINE,
  'g',
);

export const extractValidTemplateHeadings = (headings: string): string => {
  const matches = headings.match(HEADINGS_FORMAT);
  if (matches === null) {
    throw new Error(
      `Table headings do not conform to expected format:\n\n${EXPECTED_COLOR(
        'heading1 | headingN',
      )}\n\nReceived:\n\n${RECEIVED_COLOR(pretty(headings))}`,
    );
  }

  return matches[0];
};

View on GitHub (pinned to 8e6d128e4a)