{"record":{"id":"ee237ad79db49d7c","repo":"mozilla/pdf.js","slug":"second-argument-of-app-settimeout-must-be-a-number","errorCode":null,"errorMessage":"Second argument of app.setTimeOut must be a number","messagePattern":"Second argument of app\\.setTimeOut must be a number","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/scripting_api/app.js","lineNumber":637,"sourceCode":"        \"Second argument of app.setInterval must be a number\"\n      );\n    }\n    const callbackId = this._registerTimeoutCallback(cExpr);\n    this._externalCall(\"setInterval\", [callbackId, nMilliseconds]);\n    return this._registerTimeout(callbackId, true);\n  }\n\n  setTimeOut(cExpr, nMilliseconds = 0) {\n    if (cExpr && typeof cExpr === \"object\") {\n      nMilliseconds = cExpr.nMilliseconds || 0;\n      cExpr = cExpr.cExpr;\n    }\n\n    if (typeof cExpr !== \"string\") {\n      throw new TypeError(\"First argument of app.setTimeOut must be a string\");\n    }\n    if (typeof nMilliseconds !== \"number\") {\n      throw new TypeError(\"Second argument of app.setTimeOut must be a number\");\n    }\n    const callbackId = this._registerTimeoutCallback(cExpr);\n    this._externalCall(\"setTimeout\", [callbackId, nMilliseconds]);\n    return this._registerTimeout(callbackId, false);\n  }\n\n  trustedFunction() {\n    /* Not implemented */\n  }\n\n  trustPropagatorFunction() {\n    /* Not implemented */\n  }\n}\n\nexport { App };\n","sourceCodeStart":619,"sourceCodeEnd":654,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/scripting_api/app.js#L619-L654","documentation":"`app.setTimeOut` is the Acrobat-JavaScript API for scheduling a script string to run after a delay. PDF.js's scripting sandbox validates the delay (`nMilliseconds`) is a `number` before forwarding it to the host's `setTimeout`. It throws a `TypeError` (not a generic Error) because the underlying timer requires a numeric delay and silent coercion would mask script bugs. The method also accepts a single labeled-object argument `{cExpr, nMilliseconds}`, in which case a missing delay defaults to 0.","triggerScenarios":"Calling `app.setTimeOut(cExpr, nMilliseconds)` positionally with a non-number second argument: a string like \"500\", `null`, `true`, or an object. Note the default `nMilliseconds = 0` only kicks in for `undefined`; passing `null` bypasses the default so `typeof null === \"object\"` and the guard throws. The labeled-object form rarely throws because `cExpr.nMilliseconds || 0` coerces to a number.","commonSituations":"A form reads the delay from a field whose `.value` is a string; scripts ported from Acrobat that relied on implicit string-to-number coercion; passing `null` intentionally to mean 'no delay'; or a typo passing the callback function instead of a string (which instead trips the first-argument guard just above).","solutions":["Pass a numeric literal or coerce first: `app.setTimeOut(\"fn()\", Number(value))`.","If the value comes from a field, coerce at the source: `const ms = Number(this.getField(\"delay\").value) || 0;`.","Use the labeled-object form which safely defaults a missing delay: `app.setTimeOut({ cExpr: \"fn()\", nMilliseconds: 500 });`.","Guard explicitly for `null` since the default parameter does not cover it."],"exampleFix":"// before\napp.setTimeOut(\"recalc()\", \"500\");   // string -> TypeError\napp.setTimeOut(\"recalc()\", null);     // null bypasses default -> TypeError\n// after\napp.setTimeOut(\"recalc()\", 500);\napp.setTimeOut(\"recalc()\", Number(field.value) || 0);\napp.setTimeOut({ cExpr: \"recalc()\", nMilliseconds: 500 });","handlingStrategy":"type-guard","validationCode":"function safeSetTimeOut(cExpr, nMilliseconds) {\n  if (typeof nMilliseconds !== \"number\" || Number.isNaN(nMilliseconds)) {\n    nMilliseconds = Number(nMilliseconds) || 0;\n  }\n  return app.setTimeOut(cExpr, nMilliseconds);\n}","typeGuard":"const isTimeoutDelay = (v) => typeof v === \"number\" && !Number.isNaN(v);\n// usage: isTimeoutDelay(field.value) ? app.setTimeOut(fn, field.value) : app.setTimeOut(fn, 0);","tryCatchPattern":"try { app.setTimeOut(cExpr, nMilliseconds); }\ncatch (e) {\n  if (e instanceof TypeError && /setTimeOut must be a number/.test(e.message)) {\n    app.setTimeOut(cExpr, Number(nMilliseconds) || 0); // retry with coerced value\n  } else { throw e; }\n}","preventionTips":["Always coerce field-derived delay values with `Number(x) || 0` before passing them to `setTimeOut`/`setInterval`.","Remember the `nMilliseconds = 0` default only applies to `undefined`, not `null`.","Prefer the labeled-object form when juggling optional arguments."],"tags":["scripting","acrobat-api","type-validation","settimeout"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}