#7605·refine

parseSrt crashes with unhandled TypeError on leading blank lines, whitespace, or comments

Author: codeCraft-RitikCreated Sep 16, 2026Updated Sep 17, 2026
Labelsbug

Describe the bug

Describe the bug

parseSrt throws an unhandled TypeError: Cannot read properties of undefined (reading 'text') whenever an SRT file contains leading blank lines, whitespace, or metadata headers before the first numbered cue.

In packages/captions/src/parse-srt.ts:

typescript
} else if (line?.trim() === '') {
  (captions[captions.length - 1] as Caption).text = (
    captions[captions.length - 1] as Caption
  ).text.trim();
} else {
  (captions[captions.length - 1] as Caption).text += line + '\n';
}

If the file starts with an empty line or comment, captions.length is 0. Accessing captions[-1] produces undefined, and reading undefined.text crashes immediately.

Furthermore, Windows CRLF (\r\n) line breaks are not normalized, leaking trailing \r into subtitle strings, and timestamps formatted with decimal periods (00:00:01.500, standard in WebVTT and Whisper) throw Invalid timestamp in toSeconds.

Steps To Reproduce

Steps To Reproduce

  1. Call parseSrt with an SRT string containing a leading newline:
typescript
import { parseSrt } from '@remotion/captions';

const input = '\n1\n00:00:00,000 --> 00:00:02,000\nHello world\n';
parseSrt({ input });
  1. Uncaught TypeError:
TypeError: Cannot read properties of undefined (reading 'text')
    at parseSrt (packages/captions/src/parse-srt.ts:63:39)

Expected behavior

Expected behavior

parseSrt should gracefully ignore leading/trailing blank lines, normalize CRLF line breaks, and support both , and . decimal millisecond separators without crashing.

Suggested Fix

diff
--- a/packages/captions/src/parse-srt.ts
+++ b/packages/captions/src/parse-srt.ts
@@ -14,7 +14,7 @@ function toSeconds(time: string) {
 		throw new Error(`Invalid timestamp:${time}`);
 	}
 
-	const [seconds, millis] = third.split(',');
+	const [seconds, millis] = third.trim().split(/[,.]/);
 	if (!seconds) {
 		throw new Error(`Invalid timestamp:${time}`);
 	}
@@ -40,16 +40,21 @@ export type ParseSrtOutput = {
 };
 
 export const parseSrt = ({input}: ParseSrtInput): ParseSrtOutput => {
-	const inputLines = input.split('\n');
+	const normalized = input
+		.replace(/^\uFEFF/, '')
+		.replace(/\r\n/g, '\n')
+		.replace(/\r/g, '\n');
+	const inputLines = normalized.split('\n');
 	const captions: Caption[] = [];
 
 	for (let i = 0; i < inputLines.length; i++) {
 		const line = inputLines[i];
 		const nextLine = inputLines[i + 1];
-		if (line?.match(/([0-9]+)/) && nextLine?.includes(' --> ')) {
+		if (line?.match(/^\s*\d+\s*$/) && nextLine?.includes(' --> ')) {
 			const nextLineSplit = nextLine.split(' --> ');
-			const start = toSeconds(nextLineSplit[0] as string);
-			const end = toSeconds(nextLineSplit[1] as string);
+			const start = toSeconds((nextLineSplit[0] as string).trim());
+			const end = toSeconds((nextLineSplit[1] as string).trim());
 			captions.push({
 				text: '',
 				startMs: start * 1000,
@@ -60,9 +65,11 @@ export const parseSrt = ({input}: ParseSrtInput): ParseSrtOutput => {
 		} else if (line?.includes(' --> ')) {
 			continue;
 		} else if (line?.trim() === '') {
-			(captions[captions.length - 1] as Caption).text = (
-				captions[captions.length - 1] as Caption
-			).text.trim();
+			if (captions.length > 0) {
+				(captions[captions.length - 1] as Caption).text = (
+					captions[captions.length - 1] as Caption
+				).text.trim();
+			}
 		} else if (captions.length > 0) {
 			(captions[captions.length - 1] as Caption).text += line + '\n';
 		}
 	}

Packages

@remotion/captions

Additional Context

No response