{"record":{"id":"af04773faf86733f","repo":"vercel/ai","slug":"chunking-regexp-must-not-match-an-empty-string","errorCode":null,"errorMessage":"Chunking RegExp must not match an empty string.","messagePattern":"Chunking RegExp must not match an empty string\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/ai/src/generate-text/smooth-stream.ts","lineNumber":113,"sourceCode":"    }\n\n    detectChunk = buffer => {\n      const lastIndex = chunkingRegex.lastIndex;\n      chunkingRegex.lastIndex = 0;\n\n      let match: RegExpExecArray | null;\n      try {\n        match = chunkingRegex.exec(buffer);\n      } finally {\n        chunkingRegex.lastIndex = lastIndex;\n      }\n\n      if (!match) {\n        return null;\n      }\n\n      if (!match[0].length) {\n        throw new Error(`Chunking RegExp must not match an empty string.`);\n      }\n\n      return buffer.slice(0, match.index) + match[0];\n    };\n  }\n\n  return () => {\n    let buffer = '';\n    let id = '';\n    let type: 'text-delta' | 'reasoning-delta' | undefined = undefined;\n    let providerMetadata: SharedV4ProviderMetadata | undefined = undefined;\n\n    function flushBuffer(\n      controller: TransformStreamDefaultController<TextStreamPart<TOOLS>>,\n    ) {\n      if (buffer.length > 0 && type !== undefined) {\n        controller.enqueue({\n          type,","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/ai/src/generate-text/smooth-stream.ts#L95-L131","documentation":"Error thrown by smoothStream when the chunking RegExp matches an empty string at the current position (zero-length match). Zero-length matches would loop forever producing no progress, so the SDK rejects them.","triggerScenarios":"Passing a RegExp that can match empty input, e.g. /\\s*/ (star instead of plus), /(word)?/, or lookahead-only patterns like /(?=\\s)/.","commonSituations":"Using * quantifiers by habit; converting 'line' or 'word' presets to custom regex incorrectly; patterns relying solely on lookaheads without consuming characters.","solutions":["Replace * with + for consuming classes, e.g. /\\s+/ instead of /\\s*/.","Anchor the pattern so it must consume at least one character; add an alternation with a required group.","Test the RegExp against typical buffers and assert match[0].length > 0."],"exampleFix":"// before\nsmoothStream({ chunking: /\\s*/ });\n// after\nsmoothStream({ chunking: /\\s+/ });","handlingStrategy":"validation","validationCode":"if (chunking instanceof RegExp) {\n  if (''.match(chunking)?.[0] !== undefined && ''.match(chunking)![0].length === 0) {\n    throw new Error('chunking RegExp can match an empty string');\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  stream(pipeThrough(smoothStream({ chunking: myRegex })));\n} catch (e) {\n  if (e instanceof Error && e.message.includes('empty string')) {\n    // fall back to 'word' chunking\n  } else throw e;\n}","preventionTips":["Use + instead of * on consuming classes in chunking regexes.","Test every regex against '' and ensure the first match consumes characters.","Prefer built-in 'word'/'line' presets over hand-written regexes."],"tags":["streaming","smooth-stream","regex"],"backgroundTag":"regex-empty-match","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}