remotion-dev/remotion · error · Error
frame must be an integer.
Error message
frame must be an integer.
What it means
This error is thrown by a Remotion Studio MCP tool handler when the `frame` argument passed to a frame-related tool (e.g. seek/set-frame) is not a JavaScript number or not an integer. The tool schema declares `frame` as required, but MCP clients can send values that arrive as strings or floats, so the handler re-validates at runtime. It exists to prevent seeking to a non-integer frame, which has no meaning in Remotion's timeline.
Source
Thrown at packages/studio/src/components/WebMcp.tsx:1771
title: 'Seek Studio timeline',
description:
'Seek the current Remotion Studio composition to a frame. Frames past the end of the composition are clamped to the final frame.',
inputSchema: {
type: 'object',
properties: {
frame: {
type: 'integer',
minimum: 0,
description: 'The zero-based frame to seek to.',
},
},
required: ['frame'],
additionalProperties: false,
},
annotations: {readOnlyHint: false},
execute: ({frame}) => {
if (typeof frame !== 'number' || !Number.isInteger(frame)) {
throw new Error('frame must be an integer.');
}
const compositionId = currentCompositionRef.current;
if (compositionId === null) {
throw new Error('No composition is currently selected.');
}
const currentFrame = Math.min(
Math.max(0, frame),
getCurrentDuration() - 1,
);
seek(currentFrame);
return Promise.resolve({
currentFrame,
currentContent: currentContentRef.current,
});
},
},View on GitHub (pinned to b2f4e34732)
Solutions
- Ensure the MCP client sends frame as a JSON integer, not a string: pass 30, not "30"
- Round or floor computed frame values before calling the tool: Math.floor(time * fps)
- Verify the tool call arguments against the tool's input schema (frame is required, type integer) before invoking
- Check for accidental null/undefined when the frame value is derived from an optional variable
Example fix
// before
await mcp.callTool('seekToFrame', {frame: "30"}); // string
await mcp.callTool('seekToFrame', {frame: time * fps}); // may be fractional
// after
await mcp.callTool('seekToFrame', {frame: Math.floor(time * fps)}); Defensive patterns
Strategy: validation
Validate before calling
const frame = Number(rawFrame);
if (!Number.isInteger(frame)) {
// don't call the tool; fix the value first
throw new TypeError(`Expected integer frame, got ${JSON.stringify(rawFrame)}`);
}
await mcp.callTool('seekToFrame', {frame}); Type guard
const isIntegerFrame = (v: unknown): v is number => typeof v === 'number' && Number.isInteger(v);
Try / catch
try {
await mcp.callTool('seekToFrame', {frame});
} catch (err) {
if (err instanceof Error && err.message === 'frame must be an integer.') {
// coerce/round and retry once
}
throw err;
} Prevention
- Always Math.floor or Math.round computed frame values before passing them
- Never send numbers as strings in MCP tool arguments
- Validate tool args against the tool's declared input schema before invoking
When it happens
Trigger: Calling a Studio WebMCP frame tool (e.g. via an MCP client like Claude) with frame sent as a string ("30" instead of 30), a float (29.5), null/undefined, or a value that JSON-parses to a non-number. Also occurs when a client library serializes integers as strings.
Common situations: MCP client implementations that don't coerce tool arguments to the declared JSON schema types; agent prompts that quote numbers; hand-rolled MCP requests that send frame as text; time-to-frame conversions producing fractional frames.
Related errors
- Argument passed for "frame" is not a number: ${frame}
- The artifact filename ${artifact.filename} was emitted more
- Composition ${compositionName} not found.
- visible must be a boolean.
- assetPath must be a non-empty public-folder path, or an asse
AI-assisted analysis of remotion-dev/remotion@b2f4e34732 (2026-08-28).
Data as JSON: /api/errors/ccf73acbf9fb555e.
Report an issue: GitHub.