{"record":{"id":"e805f5bf46f356c9","repo":"remotion-dev/remotion","slug":"non-numeric-strings-can-only-be-interpolated-using","errorCode":null,"errorMessage":"Non-numeric strings can only be interpolated using Easing.step1","messagePattern":"Non-numeric strings can only be interpolated using Easing\\.step1","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/core/src/interpolate.ts","lineNumber":898,"sourceCode":"\toutputRange: readonly string[];\n\toptions: InterpolateOptions | undefined;\n}): string => {\n\tif (inputRange.length === 1) {\n\t\treturn outputRange[0];\n\t}\n\n\tfor (\n\t\tlet segmentIndex = 0;\n\t\tsegmentIndex < inputRange.length - 1;\n\t\tsegmentIndex++\n\t) {\n\t\tif (\n\t\t\tresolveEasingForSegment({\n\t\t\t\teasing: options?.easing,\n\t\t\t\tsegmentIndex,\n\t\t\t}) !== Easing.step1\n\t\t) {\n\t\t\tthrow new TypeError(\n\t\t\t\t'Non-numeric strings can only be interpolated using Easing.step1',\n\t\t\t);\n\t\t}\n\t}\n\n\tconst posterizedInput =\n\t\toptions?.posterize === undefined\n\t\t\t? input\n\t\t\t: Math.floor(input / options.posterize) * options.posterize;\n\tconst inputMin = inputRange[0];\n\tconst inputMax = inputRange[inputRange.length - 1];\n\tlet resolvedInput = posterizedInput;\n\n\tif (resolvedInput < inputMin) {\n\t\tif (options?.extrapolateLeft === 'identity') {\n\t\t\tthrow new TypeError(\n\t\t\t\t'extrapolateLeft: \"identity\" is not supported for non-numeric strings',\n\t\t\t);","sourceCodeStart":880,"sourceCodeEnd":916,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/core/src/interpolate.ts#L880-L916","documentation":"Thrown by interpolateDiscreteString when outputRange contains non-numeric strings (e.g. colors, URLs, arbitrary tokens that parseStringInterpolationValue cannot treat as scale/translate/rotate) and at least one segment's easing is not Easing.step1. Non-numeric strings cannot be numerically interpolated, so they must step instantly at segment boundaries.","triggerScenarios":"interpolate(t, [0, 1], [\"red\", \"blue\"]) with no options, or with easing: Easing.linear; or multi-segment [\"a\",\"b\",\"c\"] where any segment lacks Easing.step1. Remotion falls back to discrete interpolation only when numeric interpolation is impossible.","commonSituations":"Animating between colors, display values (\"block\"/\"none\"), or content strings and forgetting that discrete steps require Easing.step1; using a default easing with a string outputRange.","solutions":["Pass easing: Easing.step1 so each segment steps at its midpoint: interpolate(t, [0,1], [\"red\",\"blue\"], {easing: Easing.step1}).","For multi-segment ranges, provide an array of Easing.step1 entries, one per segment.","If you wanted smooth color animation, interpolate numeric channels (RGB) yourself or use a numeric outputRange."],"exampleFix":"// before\ninterpolate(t, [0, 1], [\"red\", \"blue\"]);\n// after\ninterpolate(t, [0, 1], [\"red\", \"blue\"], {easing: Easing.step1});","handlingStrategy":"validation","validationCode":"import {Easing} from 'remotion';\n\nfunction isDiscreteStringRange(outputRange: readonly (string | number)[]): boolean {\n  return outputRange.some((o) => typeof o === 'string')\n    && outputRange.some((o) => {\n      if (typeof o !== 'string') return false;\n      try {\n        // re-implementation of parseStringInterpolationValue's accept check\n        const parts = o.trim().split(/\\s+/);\n        return parts.some((p) => !/^([+-]?(?:\\d+\\.?\\d*|\\.\\d+))([a-zA-Z%]+)?$/.test(p)\n          && !/^(left|right|top|bottom|center)$/i.test(p));\n      } catch {\n        return true;\n      }\n    });\n}\n\nif (isDiscreteStringRange(outputRange)) {\n  const easings = options?.easing;\n  const allStep = Array.isArray(easings)\n    ? easings.every((e) => e === Easing.step1)\n    : easings === undefined || easings === Easing.step1;\n  if (!allStep) throw new Error('Non-numeric strings require easing: Easing.step1');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["For color or arbitrary-token outputRange, always pass {easing: Easing.step1}.","For multi-segment discrete ranges, pass an Easing.step1 array sized inputRange.length - 1.","If smooth motion is required, interpolate numeric channels instead of literal strings."],"tags":["interpolate","discrete-string","easing","validation"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}