vitest-dev/vitest · error · Error
expect.poll() must be called inside a test
Error message
expect.poll() must be called inside a test
What it means
`expect.poll()` needs the current test's runtime context (read from the chai `vitest-test` flag) to register its `onFinished` await-guard and to drive the retry loop tied to the test lifecycle. When no test is active there is no context to attach to, so it throws immediately. This keeps poll from silently no-oping or leaking unhandled rejections outside a test.
Source
Thrown at packages/vitest/src/integrations/chai/poll.ts:67
const state = getWorkerState()
const defaults = state.config.expect?.poll ?? {}
const {
interval = defaults.interval ?? 50,
timeout = defaults.timeout ?? 1000,
message,
} = options
// @ts-expect-error private poll access
const assertion = expect(null, message).withContext({
poll: true,
}) as Assertion
fn = fn.bind(assertion)
// injected so that domain snapshot can take over poll implementation.
chai.util.flag(assertion, '_poll.fn', fn)
chai.util.flag(assertion, '_poll.timeout', timeout)
chai.util.flag(assertion, '_poll.interval', interval)
const test = chai.util.flag(assertion, 'vitest-test') as Test | undefined
if (!test) {
throw new Error('expect.poll() must be called inside a test')
}
const proxy: any = new Proxy(assertion, {
get(target, key, receiver) {
const assertionFunction = Reflect.get(target, key, receiver)
if (typeof assertionFunction !== 'function') {
return assertionFunction instanceof chai.Assertion ? proxy : assertionFunction
}
if (key === 'assert') {
return assertionFunction
}
if (typeof key === 'string' && unsupported.includes(key)) {
throw new SyntaxError(
`expect.poll() is not supported in combination with .${key}(). Use vi.waitFor() if your assertion condition is unstable.`,
)
}View on GitHub (pinned to d568f8ce37)
Solutions
- Move the `expect.poll(...)` call inside an `it()`/`test()` body.
- If polling is needed in setup/teardown, use `vi.waitFor()` or `vi.waitUntil()` instead which do not require a test context.
- Ensure the file is actually loaded as a test file by the runner (not imported as a library).
Example fix
// before (top-level)
await expect.poll(() => store.ready).toBe(true)
// after
it('becomes ready', async () => {
await expect.poll(() => store.ready).toBe(true)
}) Defensive patterns
Strategy: validation
Validate before calling
import { getWorkerState } from 'vitest'
const inTest = (() => { try { return !!getWorkerState().current } catch { return false } })()
if (!inTest) throw new Error('call expect.poll inside an it()/test()') Prevention
- Keep all `expect.poll` calls inside `it()`/`test()` bodies.
- For setup/teardown waiting use `vi.waitFor` / `vi.waitUntil` which need no test context.
When it happens
Trigger: Calling `expect.poll(fn)` at module top level, inside `beforeAll`/`beforeEach`/`afterEach` hooks, in a plain script, or anywhere Vitest has not bound a `Test` object to the assertion's `vitest-test` flag.
Common situations: Hoisting poll usage outside tests, refactoring shared assertion helpers that run outside a test scope, or running Vitest expect API in a standalone Node script without the runner.
Related errors
- Async schema validation is not supported in asymmetric match
- expect.poll() is not supported in combination with .${key}()
- Cannot annotate tests outside of the test run. The test "${t
- Cannot take a screenshot without a test path
- stopChunkTrace cannot be called outside of the test file.
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/6fc9b2afe8efa1e9.json.
Report an issue: GitHub.