{"record":{"id":"4d40e00093c3a0e6","repo":"tastejs/todomvc","slug":"invalid-number-of-tests-n-from-env-cypress-e","errorCode":null,"errorMessage":"Invalid number of tests ${N} from env \"${Cypress.env('times')}\"","messagePattern":"Invalid number of tests (.+?) from env \"(.+?)\"","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"cypress/e2e/spec.cy.js","lineNumber":325,"sourceCode":"              return\n            }\n          })\n        ) {\n          return resolve()\n        }\n        setTimeout(checkItems, 0)\n      }\n      checkItems()\n    })\n  })\n}\n\n// to find flaky tests we are running the entire suite N times\nconst N = parseFloat(Cypress.env('times') || '1')\nconsole.log('Running tests %d time(s)', N)\nlet counter = 0\nif (!Cypress._.isFinite(N)) {\n  throw new Error(`Invalid number of tests ${N} from env \"${Cypress.env('times')}\"`)\n}\n\nCypress._.times(N, () => {\n  counter += 1\n  const countedTitle = N > 1 ? `${counter} / ${N} ${title}` : title\n  // TODO fix our runner\n  // when using same \"title\" for describe, N suites are added\n  // when using \"countedTitle\" - only the  LAST suite is added to the runner\n\n  describe(title, function () {\n    // setup these constants to match what TodoMVC does\n    let TODO_ITEM_ONE = 'buy some cheese'\n    let TODO_ITEM_TWO = 'feed the cat'\n    let TODO_ITEM_THREE = 'book a doctors appointment'\n\n    // different selectors depending on the app - some use ids, some use classes\n    let useIds\n    let selectors","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/tastejs/todomvc/blob/ff43b02e59dfa604386bb382034b2cd07c2bcd8a/cypress/e2e/spec.cy.js#L307-L343","documentation":"Thrown at cypress/e2e/spec.cy.js:325 when validating the `times` env var, which controls how many times the full suite re-runs for flake detection via `Cypress._.times(N, ...)` at line 328. The value is parsed with `parseFloat` (line 321) and then checked with `Cypress._.isFinite`; any non-numeric string yields NaN, which isFinite rejects, aborting the suite before tests register. The default of '1' only kicks in when the env var is unset or empty (the `|| '1'` fallback), not when it contains garbage.","triggerScenarios":"Passing `--env times=abc`, `--env times=all`, or `CYPRESS_times=foo`. Any value where `parseFloat` returns NaN triggers it: alphabetic strings, symbols, or a value like `--env times=` (the empty-string case is actually caught by the `|| '1'` fallback, so it must be a non-empty non-numeric token). Note `times=1.5` is valid (parseFloat -> 1.5, isFinite true) and `times=0` is valid but runs zero iterations.","commonSituations":"CI matrix variables that interpolate to a label instead of a number; a wrapper script passing `times=$REPEAT_COUNT` where the shell var is unset and expands to a flag name; copy-pasting a `--env times=many` from memory; misreading the env var as a boolean flag.","solutions":["Pass a valid number: `cypress run --env framework=react,times=3`.","Omit `times` entirely so the `|| '1'` default applies (single run).","If set via a shell/CI variable, confirm it expands to a number, e.g. `times=${REPEAT_COUNT:-1}` with REPEAT_COUNT unset or numeric.","Sanitize the value before launch (see validationCode) so a bad CI input fails fast with a clearer message than the in-spec throw."],"exampleFix":"// before\n$ CYPRESS_times=repeat cypress run\n  -> Error: Invalid number of tests NaN from env \"repeat\"\n\n// after\n$ CYPRESS_times=5 cypress run","handlingStrategy":"validation","validationCode":"// Pre-check the times env var before launching Cypress.\nconst raw = process.env.CYPRESS_times\nif (raw !== undefined && raw !== '' && !Number.isFinite(Number(raw))) {\n  console.error(`CYPRESS_times must be a number, got \"${raw}\"`)\n  process.exit(1)\n}\nconst n = Number(raw || '1')\nif (n < 0 || !Number.isInteger(n)) {\n  console.warn(`CYPRESS_times=${n} may behave unexpectedly; Cypress._.times expects a non-negative integer.`)\n}","typeGuard":"// Narrow a parsed times value to a usable loop count.\nconst isFiniteTimes = (v) => {\n  const n = typeof v === 'number' ? v : parseFloat(v)\n  return Number.isFinite(n) && n >= 0\n}","tryCatchPattern":null,"preventionTips":["Treat `times` as opt-in: leave it unset for normal runs and only set it in a dedicated flake-hunt job.","Always coerce through `Number()` and validate with Number.isFinite in any wrapper script.","In CI, default the variable explicitly: `CYPRESS_times: ${REPEAT_COUNT:-1}`.","Remember parseFloat('1.5') is accepted (fractional) — if you want whole runs, also check Number.isInteger."],"tags":["cypress","configuration","validation","environment-variable","numeric-parse"],"backgroundTag":null,"analyzedSha":"ff43b02e59dfa604386bb382034b2cd07c2bcd8a","analyzedAt":"2026-08-13T10:00:20.798Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}