{"record":{"id":"ba32d582ffc659d9","repo":"remotion-dev/remotion","slug":"expiresin-should-be-a-number-but-is-json-stri","errorCode":null,"errorMessage":"'expiresIn' should be a number, but is ${JSON.stringify(presignExpiration)}","messagePattern":"'expiresIn' should be a number, but is (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/lambda-client/src/validate-presign-expiration.ts","lineNumber":10,"sourceCode":"const MAX_PRESIGN_EXPIRATION = 604800;\nconst MIN_PRESIGN_EXPIRATION = 1;\n\nexport const validatePresignExpiration = (presignExpiration: unknown) => {\n\tif (typeof presignExpiration === 'undefined' || presignExpiration === null) {\n\t\treturn;\n\t}\n\n\tif (typeof presignExpiration !== 'number') {\n\t\tthrow new TypeError(\n\t\t\t`'expiresIn' should be a number, but is ${JSON.stringify(\n\t\t\t\tpresignExpiration,\n\t\t\t)}`,\n\t\t);\n\t}\n\n\tif (Number.isNaN(presignExpiration)) {\n\t\tthrow new TypeError(`'expiresIn' should not be NaN, but is NaN`);\n\t}\n\n\tif (!Number.isFinite(presignExpiration)) {\n\t\tthrow new TypeError(\n\t\t\t`'expiresIn' should be finite but is ${presignExpiration}`,\n\t\t);\n\t}\n\n\tif (presignExpiration % 1 !== 0) {\n\t\tthrow new TypeError(","sourceCodeStart":1,"sourceCodeEnd":28,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/lambda-client/src/validate-presign-expiration.ts#L1-L28","documentation":"Thrown by validatePresignExpiration() in @remotion/lambda-client when the optional `expiresIn` (presignExpiration) argument is defined, non-null, but not a number. The value controls how long a presigned render-output URL stays valid; it must be an integer number of seconds.","triggerScenarios":"Calling presignUrl() / renderMediaOnLambda() with expiresIn passed as a string (e.g. \"3600\" from an env var), a boolean, or an object. The undefined/null early-return means only other types trip this.","commonSituations":"Reading expiresIn from an environment variable or JSON config without Number() coercion; forwarding a query-string value (always a string) straight into the API.","solutions":["Coerce the value to a number before passing: Number(expiresIn) and check Number.isFinite().","If the source is a string env var, parse it explicitly: parseInt(process.env.EXPIRES_IN, 10).","Omit expiresIn entirely if you want the default rather than passing a string."],"exampleFix":"// before\nawait renderMediaOnLambda({..., opts: {expiresIn: process.env.EXPIRES_IN}});\n\n// after\nconst expiresIn = process.env.EXPIRES_IN ? parseInt(process.env.EXPIRES_IN, 10) : undefined;\nawait renderMediaOnLambda({..., opts: {expiresIn}});","handlingStrategy":"type-guard","validationCode":"const expiresIn = typeof raw === 'string' ? parseInt(raw, 10) : raw;\nconst safeExpires = typeof expiresIn === 'number' && Number.isFinite(expiresIn) ? expiresIn : undefined;\nawait renderMediaOnLambda({..., opts: {expiresIn: safeExpires}});","typeGuard":"const isExpiresIn = (v: unknown): v is number =>\n  (typeof v === 'undefined' || v === null) ? false : typeof v === 'number' && Number.isFinite(v) && v % 1 === 0 && v >= 1 && v <= 604800;","tryCatchPattern":null,"preventionTips":["Coerce expiresIn from any string source (env var, query string, JSON) with parseInt/Number before passing.","Pass undefined rather than a string when you want the library default.","Centralize env-var parsing in one module that returns correctly-typed values."],"tags":["lambda","validation","typescript","presign","configuration"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}