{"id":"6fc9b2afe8efa1e9","repo":"vitest-dev/vitest","slug":"expect-poll-must-be-called-inside-a-test","errorCode":null,"errorMessage":"expect.poll() must be called inside a test","messagePattern":"expect\\.poll\\(\\) must be called inside a test","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/integrations/chai/poll.ts","lineNumber":67,"sourceCode":"    const state = getWorkerState()\n    const defaults = state.config.expect?.poll ?? {}\n    const {\n      interval = defaults.interval ?? 50,\n      timeout = defaults.timeout ?? 1000,\n      message,\n    } = options\n    // @ts-expect-error private poll access\n    const assertion = expect(null, message).withContext({\n      poll: true,\n    }) as Assertion\n    fn = fn.bind(assertion)\n    // injected so that domain snapshot can take over poll implementation.\n    chai.util.flag(assertion, '_poll.fn', fn)\n    chai.util.flag(assertion, '_poll.timeout', timeout)\n    chai.util.flag(assertion, '_poll.interval', interval)\n    const test = chai.util.flag(assertion, 'vitest-test') as Test | undefined\n    if (!test) {\n      throw new Error('expect.poll() must be called inside a test')\n    }\n    const proxy: any = new Proxy(assertion, {\n      get(target, key, receiver) {\n        const assertionFunction = Reflect.get(target, key, receiver)\n\n        if (typeof assertionFunction !== 'function') {\n          return assertionFunction instanceof chai.Assertion ? proxy : assertionFunction\n        }\n\n        if (key === 'assert') {\n          return assertionFunction\n        }\n\n        if (typeof key === 'string' && unsupported.includes(key)) {\n          throw new SyntaxError(\n            `expect.poll() is not supported in combination with .${key}(). Use vi.waitFor() if your assertion condition is unstable.`,\n          )\n        }","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/integrations/chai/poll.ts#L49-L85","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before (top-level)\nawait expect.poll(() => store.ready).toBe(true)\n// after\nit('becomes ready', async () => {\n  await expect.poll(() => store.ready).toBe(true)\n})","handlingStrategy":"validation","validationCode":"import { getWorkerState } from 'vitest'\nconst inTest = (() => { try { return !!getWorkerState().current } catch { return false } })()\nif (!inTest) throw new Error('call expect.poll inside an it()/test()')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep all `expect.poll` calls inside `it()`/`test()` bodies.","For setup/teardown waiting use `vi.waitFor` / `vi.waitUntil` which need no test context."],"tags":["expect","poll","test-context","async"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}