{"record":{"id":"ccf73acbf9fb555e","repo":"remotion-dev/remotion","slug":"frame-must-be-an-integer","errorCode":null,"errorMessage":"frame must be an integer.","messagePattern":"frame must be an integer\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/studio/src/components/WebMcp.tsx","lineNumber":1771,"sourceCode":"\t\t\t\t\ttitle: 'Seek Studio timeline',\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t'Seek the current Remotion Studio composition to a frame. Frames past the end of the composition are clamped to the final frame.',\n\t\t\t\t\tinputSchema: {\n\t\t\t\t\t\ttype: 'object',\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\tframe: {\n\t\t\t\t\t\t\t\ttype: 'integer',\n\t\t\t\t\t\t\t\tminimum: 0,\n\t\t\t\t\t\t\t\tdescription: 'The zero-based frame to seek to.',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\trequired: ['frame'],\n\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t},\n\t\t\t\t\tannotations: {readOnlyHint: false},\n\t\t\t\t\texecute: ({frame}) => {\n\t\t\t\t\t\tif (typeof frame !== 'number' || !Number.isInteger(frame)) {\n\t\t\t\t\t\t\tthrow new Error('frame must be an integer.');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst compositionId = currentCompositionRef.current;\n\t\t\t\t\t\tif (compositionId === null) {\n\t\t\t\t\t\t\tthrow new Error('No composition is currently selected.');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst currentFrame = Math.min(\n\t\t\t\t\t\t\tMath.max(0, frame),\n\t\t\t\t\t\t\tgetCurrentDuration() - 1,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tseek(currentFrame);\n\t\t\t\t\t\treturn Promise.resolve({\n\t\t\t\t\t\t\tcurrentFrame,\n\t\t\t\t\t\t\tcurrentContent: currentContentRef.current,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t},","sourceCodeStart":1753,"sourceCodeEnd":1789,"githubUrl":"https://github.com/remotion-dev/remotion/blob/b2f4e34732f3c6c222ea029413e22dc402218cd7/packages/studio/src/components/WebMcp.tsx#L1753-L1789","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait mcp.callTool('seekToFrame', {frame: \"30\"}); // string\nawait mcp.callTool('seekToFrame', {frame: time * fps}); // may be fractional\n\n// after\nawait mcp.callTool('seekToFrame', {frame: Math.floor(time * fps)});","handlingStrategy":"validation","validationCode":"const frame = Number(rawFrame);\nif (!Number.isInteger(frame)) {\n  // don't call the tool; fix the value first\n  throw new TypeError(`Expected integer frame, got ${JSON.stringify(rawFrame)}`);\n}\nawait mcp.callTool('seekToFrame', {frame});","typeGuard":"const isIntegerFrame = (v: unknown): v is number =>\n  typeof v === 'number' && Number.isInteger(v);","tryCatchPattern":"try {\n  await mcp.callTool('seekToFrame', {frame});\n} catch (err) {\n  if (err instanceof Error && err.message === 'frame must be an integer.') {\n    // coerce/round and retry once\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["mcp","validation","frame","studio","type-coercion"],"backgroundTag":"tool-argument-validation","analyzedSha":"b2f4e34732f3c6c222ea029413e22dc402218cd7","analyzedAt":"2026-08-28T15:39:09.316Z","contentChangedAt":"2026-08-28T15:39:09.316Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}