tastejs/todomvc · critical · Error

Invalid number of tests ${N} from env "${Cypress.env('times'

Error message

Invalid number of tests ${N} from env "${Cypress.env('times')}"

What it means

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.

Source

Thrown at cypress/e2e/spec.cy.js:325

              return
            }
          })
        ) {
          return resolve()
        }
        setTimeout(checkItems, 0)
      }
      checkItems()
    })
  })
}

// to find flaky tests we are running the entire suite N times
const N = parseFloat(Cypress.env('times') || '1')
console.log('Running tests %d time(s)', N)
let counter = 0
if (!Cypress._.isFinite(N)) {
  throw new Error(`Invalid number of tests ${N} from env "${Cypress.env('times')}"`)
}

Cypress._.times(N, () => {
  counter += 1
  const countedTitle = N > 1 ? `${counter} / ${N} ${title}` : title
  // TODO fix our runner
  // when using same "title" for describe, N suites are added
  // when using "countedTitle" - only the  LAST suite is added to the runner

  describe(title, function () {
    // setup these constants to match what TodoMVC does
    let TODO_ITEM_ONE = 'buy some cheese'
    let TODO_ITEM_TWO = 'feed the cat'
    let TODO_ITEM_THREE = 'book a doctors appointment'

    // different selectors depending on the app - some use ids, some use classes
    let useIds
    let selectors

View on GitHub (pinned to ff43b02e59)

Solutions

  1. Pass a valid number: `cypress run --env framework=react,times=3`.
  2. Omit `times` entirely so the `|| '1'` default applies (single run).
  3. 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.
  4. Sanitize the value before launch (see validationCode) so a bad CI input fails fast with a clearer message than the in-spec throw.

Example fix

// before
$ CYPRESS_times=repeat cypress run
  -> Error: Invalid number of tests NaN from env "repeat"

// after
$ CYPRESS_times=5 cypress run
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check the times env var before launching Cypress.
const raw = process.env.CYPRESS_times
if (raw !== undefined && raw !== '' && !Number.isFinite(Number(raw))) {
  console.error(`CYPRESS_times must be a number, got "${raw}"`)
  process.exit(1)
}
const n = Number(raw || '1')
if (n < 0 || !Number.isInteger(n)) {
  console.warn(`CYPRESS_times=${n} may behave unexpectedly; Cypress._.times expects a non-negative integer.`)
}

Type guard

// Narrow a parsed times value to a usable loop count.
const isFiniteTimes = (v) => {
  const n = typeof v === 'number' ? v : parseFloat(v)
  return Number.isFinite(n) && n >= 0
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of tastejs/todomvc@ff43b02e59 (2026-08-13). Data as JSON: /api/errors/4d40e00093c3a0e6. Report an issue: GitHub.