remotion-dev/remotion · error · Error

Not enough bytes left to parse EBML - this should not happen

Error message

Not enough bytes left to parse EBML - this should not happen

What it means

parseEbml reads the next EBML element ID via iterator.getMatroskaSegmentId(); if that returns null there were not enough bytes for even the element-id octets. The 'this should not happen' comment means the contract is that callers (expectSegment) verify bytes remain before recursing into parseEbml. Reaching this throw means the byte stream ended mid-element.

Source

Thrown at packages/media-parser/src/containers/webm/parse-ebml.ts:26

	getTrack,
	NO_CODEC_PRIVATE_SHOULD_BE_DERIVED_FROM_SPS,
} from './make-track';
import type {PossibleEbml} from './segments/all-segments';
import {ebmlMap} from './segments/all-segments';
import type {WebmRequiredStatesForProcessing} from './state-for-processing';

export type Prettify<T> = {
	[K in keyof T]: T[K];
} & {};

export const parseEbml = async (
	iterator: BufferIterator,
	statesForProcessing: WebmRequiredStatesForProcessing | null,
	logLevel: MediaParserLogLevel,
): Promise<Prettify<PossibleEbml> | null> => {
	const hex = iterator.getMatroskaSegmentId();
	if (hex === null) {
		throw new Error(
			'Not enough bytes left to parse EBML - this should not happen',
		);
	}

	const off = iterator.counter.getOffset();
	const size = iterator.getVint();
	const minVintWidth = iterator.counter.getOffset() - off;

	if (size === null) {
		throw new Error(
			'Not enough bytes left to parse EBML - this should not happen',
		);
	}

	const hasInMap = ebmlMap[hex as keyof typeof ebmlMap];

	if (!hasInMap) {
		Log.verbose(logLevel, `Unknown EBML hex ID ${JSON.stringify(hex)}`);

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Verify the source URL/blob is fully downloadable and that Content-Length matches the actual bytes.
  2. If using a custom reader, ensure it does not resolve read() before yielding all requested bytes unless truly at EOF, and that EOF is reached cleanly after the Segment closes.
  3. Re-fetch or re-encode the file from a known-good source.
  4. Migrate to Mediabunny, which has a different, more tolerant streaming implementation.

Example fix

// before
await parseMedia({src: partiallyDownloadedUrl, fields: {durationInSeconds: true}});

// after - ensure the reader sees the complete file
const buf = await fetch(url).then(r => r.arrayBuffer());
await parseMedia({src: new Blob([buf]), fields: {durationInSeconds: true}});
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the full byte range is available before parsing.
const res = await fetch(url);
if (!res.ok || Number(res.headers.get('content-length')) !== realExpectedLength) {
  throw new Error('source not fully available');
}
const buf = new Uint8Array(await res.arrayBuffer());
// only parse once the whole file is buffered
await parseMedia({src: buf});

Try / catch

try {
  await parseMedia({src, fields: {durationInSeconds: true}});
} catch (err) {
  if (err instanceof Error && /Not enough bytes left to parse EBML/.test(err.message)) {
    throw new Error('Truncated media stream; re-download or verify Content-Length.', {cause: err});
  }
  throw err;
}

Prevention

When it happens

Trigger: A truncated WebM/MKV stream (download cut off, partial fetch, aborted reader), or an internal caller that recurses into parseEbml after the iterator has been consumed past the declared container size. Also reachable via a corrupted size VInt that points beyond EOF.

Common situations: Network reader returning a short body, range requests that stop early, file served with wrong Content-Length, interrupted Blob/ReadableStream, or a seek into a region the reader cannot supply.

Related errors


AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12). Data as JSON: /api/errors/7186e8e5ac244df6. Report an issue: GitHub.