remotion-dev/remotion · error · Error
onError was used but did not return an "action" field. See d
Error message
onError was used but did not return an "action" field. See docs for this API on how to use onError.
What it means
When a parse iteration throws and an onError callback was supplied, parseLoop awaits onError(e) and inspects the returned object. If that object has no `action` field the parser throws, because it cannot decide whether to fail or continue. The contract requires onError to return `{action: 'fail' | 'download'}`.
Source
Thrown at packages/media-parser/src/parse-loop.ts:149
iterator: state.iterator,
logLevel: state.logLevel,
mode: state.mode,
contentLength: state.contentLength,
seekInfiniteLoop: state.seekInfiniteLoop,
currentReader: state.currentReader,
readerInterface: state.readerInterface,
fields: state.fields,
src: state.src,
discardReadBytes: state.discardReadBytes,
prefetchCache: state.prefetchCache,
isoState: state.iso,
});
state.timings.timeSeeking += Date.now() - seekStart;
}
} catch (e) {
const err = await onError(e as Error);
if (!err.action) {
throw new Error(
'onError was used but did not return an "action" field. See docs for this API on how to use onError.',
);
}
if (err.action === 'fail') {
throw e;
}
if (err.action === 'download') {
state.errored = e as Error;
Log.verbose(
state.logLevel,
'Error was handled by onError and deciding to continue.',
);
}
}
}
View on GitHub (pinned to 78fe4bb3fd)
Solutions
- Make onError return an object with action: `{action: 'fail'}` to re-throw, or `{action: 'download'}` to continue parsing.
- If you only want to log, return `{action: 'fail'}` after logging so the original error still propagates.
- Add a TypeScript annotation `onError: ParseMediaOnError` so a missing action is caught at compile time.
- Read the media-parser onError docs for the full return contract.
Example fix
// before
await parseMedia({src, onError: (e) => console.error(e), fields: {}});
// after
await parseMedia({
src,
onError: (e) => {
console.error(e);
return {action: 'fail'};
},
fields: {},
}); Defensive patterns
Strategy: type-guard
Validate before calling
// Validate your onError returns the right shape before passing it in
function makeOnError(log: (e: Error) => void) {
return (e: Error): {action: 'fail' | 'download'} => {
log(e);
return {action: 'fail'};
};
} Type guard
import type {ParseMediaOnError} from '@remotion/media-parser';
const isOnErrorResult = (v: unknown): v is {action: 'fail' | 'download'} =>
typeof v === 'object' && v !== null &&
(v as any).action === 'fail' || (v as any).action === 'download'; Try / catch
// Not applicable — this error IS the result of a bad onError contract.
// Fix: ensure onError always returns {action: 'fail' | 'download'}.
await parseMedia({
src,
onError: (e) => { console.error(e); return {action: 'fail'}; },
fields: {},
}); Prevention
- Always return {action: 'fail'} or {action: 'download'} from onError.
- Type onError as ParseMediaOnError so a missing action is a compile error.
- If you only log, still return {action: 'fail'} to propagate the original error.
- Read the onError docs for the full recovery contract.
When it happens
Trigger: Supplying an onError that returns undefined, null, `{}`, or an object missing the action key — e.g. `onError: (e) => console.error(e)` (returns undefined) or `onError: () => ({retry: true})`.
Common situations: Treating onError like a logging callback instead of a recovery decision function; copy-pasting from examples that omit the return; migration from an older API shape.
Related errors
- No "src" provided
- bundle() was called without arguments
- bundle() was called without the `entryPoint` option
- "${name}" must be greater than 0, but got ${JSON.stringify(v
- "${name}" must be greater than or equal to 0, but got ${JSON
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/8a339d5cb2b30223.
Report an issue: GitHub.