{"record":{"id":"463186aedbcd7b7a","repo":"remotion-dev/remotion","slug":"name-must-be-greater-than-or-equal-to-0-but-463186","errorCode":null,"errorMessage":"\"${name}\" must be greater than or equal to 0, but got ${JSON.stringify(value)}","messagePattern":"\"(.+?)\" must be greater than or equal to 0, but got (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/effects/src/rings.ts","lineNumber":150,"sourceCode":"\t\tcenter: [...(p.center ?? DEFAULT_CENTER)] as RingsCenter,\n\t\tthickness,\n\t\tspacing: thickness + gap,\n\t\toffset: p.offset ?? DEFAULT_OFFSET,\n\t\tmaskToSourceAlpha: p.maskToSourceAlpha ?? DEFAULT_MASK_TO_SOURCE_ALPHA,\n\t};\n};\n\nconst validatePositive = (value: number, name: string): void => {\n\tif (value <= 0) {\n\t\tthrow new TypeError(\n\t\t\t`\"${name}\" must be greater than 0, but got ${JSON.stringify(value)}`,\n\t\t);\n\t}\n};\n\nconst validateNonNegative = (value: number, name: string): void => {\n\tif (value < 0) {\n\t\tthrow new TypeError(\n\t\t\t`\"${name}\" must be greater than or equal to 0, but got ${JSON.stringify(value)}`,\n\t\t);\n\t}\n};\n\nconst validateColors = (colors: unknown): void => {\n\tif (colors === undefined) {\n\t\treturn;\n\t}\n\n\tif (!Array.isArray(colors) || colors.length < 2) {\n\t\tthrow new TypeError(\n\t\t\t`\"colors\" must be an array with at least 2 colors, but got ${JSON.stringify(colors)}`,\n\t\t);\n\t}\n\n\tfor (let i = 0; i < colors.length; i++) {\n\t\tassertRequiredColor(colors[i], `colors[${i}]`);","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/effects/src/rings.ts#L132-L168","documentation":"Thrown by the `rings()` effect when its `gap` prop resolves to a negative number. `gap` is the transparent pixel gap between consecutive colored rings and the shader treats it as an unsigned distance, so the effect rejects any value below 0. The check runs in `validateRingsParams` via the shared `validateNonNegative` helper, after `gap` is defaulted to `0` when omitted.","triggerScenarios":"Call `rings({gap: -5})`, or animate `gap` with an interpolate() whose output range dips below 0 (e.g. `interpolate(frame, [0, 30], [10, -10])`). Also triggered by feeding `gap` from an unvalidated external input such as a JSON config or a slider that allows negatives.","commonSituations":"Animating the gap closed and overshooting into negatives; passing through a computed value like `gap: spacing - margin` where the subtraction underflows; reusing a numeric field from another effect (e.g. an `offset` that may be signed) as `gap` without clamping.","solutions":["Set `gap` to a non-negative number, or omit it to use the default of `0`.","Clamp animated values with `Math.max(0, value)` or use `interpolate(..., {extrapolateLeft: 'clamp'})`.","Validate external/serialized input before passing it to `rings()`."],"exampleFix":"// before\nrings({gap: interpolate(frame, [0, 30], [10, -10])});\n// after\nrings({\n  gap: interpolate(frame, [0, 30], [10, 0], {extrapolateRight: 'clamp'}),\n});","handlingStrategy":"validation","validationCode":"const validateRingsGap = (gap: unknown): number | undefined => {\n  if (gap === undefined) return undefined;\n  if (typeof gap !== 'number' || !Number.isFinite(gap) || gap < 0) {\n    throw new TypeError(`gap must be a finite non-negative number, got ${String(gap)}`);\n  }\n  return gap;\n};\n\n// before calling rings()\nconst safeGap = validateRingsGap(myGap);\nrings({gap: safeGap});","typeGuard":"const isNonNegativeFinite = (v: unknown): v is number =>\n  typeof v === 'number' && Number.isFinite(v) && v >= 0;","tryCatchPattern":null,"preventionTips":["Always clamp animated `gap` output ranges with `extrapolateLeft: 'clamp'`.","Treat `gap` as an unsigned pixel distance in your own types.","Run a single `validateRingsGap` helper on serialized/external input before composing."],"tags":["validation","rings-effect","effects","typescript","params"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}