{"record":{"id":"49965f70bc1ab9ba","repo":"vitest-dev/vitest","slug":"hoistednodes-size-call-plural-s-in","errorCode":null,"errorMessage":"${hoistedNodes.size} call${plural ? 's' : ''} in \"${relative(options.root || process.cwd(), id)}\" ${plural ? 'were' : 'was'} defined outside of the module's top level scope:","messagePattern":"(.+?) call(.+?) in \"(.+?)\" (.+?) defined outside of the module's top level scope:","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mocker/src/node/hoistMocks.ts","lineNumber":526,"sourceCode":"      const locations = createIndexLocationsMap(code)\n      const map = options.getMap && new TraceMap(options.getMap() as any)\n      const plural = hoistedNodes.size > 1\n      const message = [\n        `${hoistedNodes.size} call${plural ? 's' : ''} in \"${relative(options.root || process.cwd(), id)}\" ${plural ? 'were' : 'was'} defined outside of the module's top level scope:`,\n        '',\n        ...Array.from(hoistedNodes, (invalidNode) => {\n          const currentLocation = locations.get(invalidNode.start)\n          const originalLocation = map && currentLocation && originalPositionFor(map, currentLocation)\n          const location = originalLocation?.column != null && originalLocation?.line != null\n            ? ` at ${relative(options.root || process.cwd(), id)}:${originalLocation.line}:${originalLocation.column + 1}`\n            : ''\n          return `- ${getNodeName(getNodeCall(invalidNode))}${location}`\n        }),\n        '',\n        `Although ${plural ? 'they appear nested, they' : 'it appears nested, it'} will be hoisted and executed before anything in this file. Move ${plural ? 'them' : 'it'} to the top level to reflect ${plural ? 'their' : 'its'} actual execution order.`,\n        'See: https://vitest.dev/guide/mocking/modules#how-it-works',\n      ].join('\\n')\n      throw new Error(message)\n    }\n  }\n\n  // hoist vi.mock/vi.hoisted\n  for (const node of arrayNodes) {\n    const end = getNodeTail(code, node)\n    // don't hoist into itself if it's already at the top\n    if (hoistIndex === end || hoistIndex === node.start) {\n      hoistIndex = end\n    }\n    else {\n      s.move(node.start, end, hoistIndex)\n    }\n  }\n\n  // hoist actual dynamic imports last so they are inserted after all hoisted mocks\n  for (const { node: importNode, id: importId } of imports) {\n    const source = importNode.source.value as string","sourceCodeStart":508,"sourceCodeEnd":544,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/mocker/src/node/hoistMocks.ts#L508-L544","documentation":"Thrown by `hoistMocks` after AST analysis if any `vi.mock`/`vi.hoisted` call node remains in the `hoistedNodes` set after removing top-level body nodes — meaning the call is lexically nested inside a function, block, conditional, or loop rather than at module top level. Because Vitest hoists these calls to the top of the file regardless of where they are written, a nested call would execute before the enclosing scope's other statements, which is surprising and error-prone, so Vitest rejects it with a detailed, located message.","triggerScenarios":"Writing `vi.mock(...)` or `vi.hoisted(...)` inside a function body, `if` block, `try/catch`, loop, or any non-top-level scope. Note: `vi.doMock`/`vi.doUnmock` are intentionally NOT hoisted and are allowed inside nested scopes.","commonSituations":"Conditionally mocking inside `if` branches or helper functions; mocking inside `beforeEach`/`beforeAll`; wrapping `vi.mock` in a utility that tests call; copy-pasting a mock into a nested describe block's closure.","solutions":["Move the `vi.mock`/`vi.hoisted` call to the top level of the test file (module scope).","If you need conditional or runtime-dependent mocking inside a scope, use `vi.doMock`/`vi.doUnmock` instead — these are not hoisted.","Use `vi.hoisted` at the top level to share variables between the factory and the test when hoisting reorders execution.","If the call is already at the top level, check for an editor/auto-formatter that wrapped it in an IIFE or block."],"exampleFix":"// before — vi.mock nested inside a conditional\nif (process.env.CI) {\n  vi.mock('./mod', () => ({ fn: vi.fn() }))\n}\n// after — top-level mock; use vi.hoisted for shared state\nvi.mock('./mod', () => ({ fn: vi.fn() }))","handlingStrategy":"validation","validationCode":"// Lint test files for nested vi.mock/vi.hoisted calls before running.\n// Example ESLint rule logic: flag any vi.mock/vi.hoisted call whose AST depth is > 1 (not a direct child of Program).","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep all vi.mock and vi.hoisted calls at module top level.","Use vi.doMock/vi.doUnmock for runtime-dependent or nested mocking.","Use vi.hoisted at the top level to share state between the factory and the test body."],"tags":["hoist-mocks","vi-mock","scope","top-level"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}