mastra-ai/mastra · error · TypeError

The chunking RegExp must match a non-empty string.

Error message

The chunking RegExp must match a non-empty string.

What it means

When a RegExp is used as the chunking strategy, createChunkDetector builds the detected chunk as everything before the match plus the match itself. If that combined string is empty — only possible when the regex matches at index 0 and matches the empty string (a zero-width match) — no progress can be made splitting the buffer and the smoothing loop would stall, so the library throws a TypeError.

Source

Thrown at packages/core/src/stream/smooth-stream.ts:90

  }

  const pattern = typeof chunking === 'string' ? CHUNKING_PATTERNS[chunking] : chunking;

  if (!(pattern instanceof RegExp)) {
    throw new TypeError('chunking must be "word", "line", a RegExp, an Intl.Segmenter, or a chunk detector function.');
  }

  return buffer => {
    pattern.lastIndex = 0;
    const match = pattern.exec(buffer);

    if (!match) {
      return null;
    }

    const detected = buffer.slice(0, match.index) + match[0];
    if (!detected.length) {
      throw new TypeError('The chunking RegExp must match a non-empty string.');
    }

    return detected;
  };
}

/**
 * Creates a transform stream that buffers text and reasoning deltas and emits
 * them in consistent, delayed chunks. Other stream parts pass through without
 * modification after any buffered content has been emitted.
 *
 * @experimental This API may change in a future release.
 */
export function smoothStream<OUTPUT = undefined>({
  delayInMs = 10,
  chunking = 'word',
}: SmoothStreamOptions = {}): TransformStream<ChunkType<OUTPUT>, ChunkType<OUTPUT>> {
  const detectChunk = createChunkDetector(chunking);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Replace * with + quantifiers so the match consumes at least one character (e.g. /\s+/ instead of /\s*/).
  2. If splitting at boundaries, include the boundary character itself in the match (e.g. /[.,!?]\s/ instead of a lookahead alone).
  3. Test your regex against typical buffered text to confirm match[0].length > 0 for every possible match.

Example fix

// before
smoothStream({ model, chunking: /\s*/ }); // zero-width match -> throws

// after
smoothStream({ model, chunking: /\s+/ });
Defensive patterns

Strategy: validation

Validate before calling

function isNonEmptyMatchingRegex(re: RegExp, sample = 'a b c'): boolean {
  re.lastIndex = 0;
  const m = re.exec(sample);
  return !!m && m[0].length > 0;
}

Type guard

function matchIsNonEmpty(m: RegExpExecArray | null): m is RegExpExecArray {
  return m !== null && m[0].length > 0;
}

Try / catch

try {
  streamText({ model, smoothStream: { chunking: myRegex } });
} catch (err) {
  if (err instanceof TypeError && err.message.includes('RegExp must match a non-empty string')) {
    console.error('regex has zero-width matches; replace * with + or include consumed chars');
  } else throw err;
}

Prevention

When it happens

Trigger: Calling smoothStream({ chunking: /someZeroWidthRegex/ }) where the pattern can match the empty string at the start of the buffer, e.g. /\s*/, /(?=\s)/, /x*/, or a regex composed entirely of optional groups.

Common situations: Writing split-on-boundary regexes with * instead of + quantifiers; using lookahead-only patterns; adapting a regex from another language where zero-width match semantics differed.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/9780fb84628e2cd4. Report an issue: GitHub.