facebook/relay · error · Error
This version of `act` requires Jest's timer mocks (i.e. jest
Error message
This version of `act` requires Jest's timer mocks (i.e. jest.useFakeTimers).
What it means
After confirming the mock Scheduler, internal act() also asserts that Jest's timer mocks are installed: setTimeout._isMockFunction must be true. act() flushes work using timer-mocked setTimeout, so with real timers (jest.useFakeTimers not called) it cannot guarantee synchronous flushing and throws.
Source
Thrown at packages/react-relay/jest-react/internalAct.js:46
// This doesn't require a return value neither from the handler nor the
// then function.
interface Thenable<out R> {
then<U>(
onFulfill: (value: R) => void | Thenable<U> | U,
onReject: (error: unknown) => void | Thenable<U> | U,
): void | Thenable<U>;
}
let actingUpdatesScopeDepth = 0;
function act<T>(scope: () => Thenable<T> | T): Thenable<T> {
if (Scheduler.unstable_flushAllWithoutAsserting === undefined) {
throw Error(
'This version of `act` requires a special mock build of Scheduler.',
);
}
if (setTimeout._isMockFunction !== true) {
throw Error(
"This version of `act` requires Jest's timer mocks " +
'(i.e. jest.useFakeTimers).',
);
}
const previousIsActEnvironment = global.IS_REACT_ACT_ENVIRONMENT;
const previousActingUpdatesScopeDepth = actingUpdatesScopeDepth;
actingUpdatesScopeDepth++;
if (__DEV__ && actingUpdatesScopeDepth === 1) {
// Because this is not the "real" `act`, we set this to `false` so React
// knows not to fire `act` warnings.
global.IS_REACT_ACT_ENVIRONMENT = false;
}
const unwind = () => {
if (__DEV__ && actingUpdatesScopeDepth === 1) {
global.IS_REACT_ACT_ENVIRONMENT = previousIsActEnvironment;
}View on GitHub (pinned to 668b1b85e0)
Solutions
- Call jest.useFakeTimers() at the top of the test (or in setupFilesAfterAll) before using Relay test APIs
- If using modern fake timers, verify jest-react's expected version compatibility or configure legacy fake timers (jest.useFakeTimers('legacy'))
- Ensure all suites using RelayTestingEnvironment share a setup file that installs fake timers
- Check that no other mocking library replaces global setTimeout without setting _isMockFunction
Example fix
// before
it('renders', () => {
const env = createMockEnvironment();
TestRenderer.create(<Component />);
});
// after
it('renders', () => {
jest.useFakeTimers();
const env = createMockEnvironment();
TestRenderer.create(<Component />);
}); Defensive patterns
Strategy: try-catch
Validate before calling
// at top of test file
if ((global.setTimeout as any)._isMockFunction !== true) {
jest.useFakeTimers();
} Try / catch
try {
env.execute({...}).toPromise();
jest.runAllTimers();
} catch (e) {
if (String(e.message).includes('timer mocks')) {
jest.useFakeTimers();
}
throw e;
} Prevention
- Install jest.useFakeTimers() in a shared setup file for all Relay test suites
- Confirm fake-timer implementation sets setTimeout._isMockFunction
- Avoid mixing sinon/vitest fake timers with jest-react
When it happens
Trigger: Invoking RelayTestingEnvironment APIs (testRender, resolve, etc.) inside a test that runs with real timers — no jest.useFakeTimers() call, or fake timers enabled only after act() was first invoked, or using a test runner whose fake timers don't set setTimeout._isMockFunction (e.g. some sinon/lolex configurations).
Common situations: Migrating tests to modern fake-timers APIs where the underscore mock flag differs, suites mixing vi.useFakeTimers (vitest) or sinon fake timers with jest-react, or forgetting fake timers in setupFilesAfterEach for a specific test file.
Related errors
- This version of `act` requires a special mock build of Sched
- BabelPluginRelay: Expected plugin context to include "types"
- BabelPluginRelay: Expected exactly one definition per graphq
- BabelPluginRelay: Expected a fragment, mutation, query, or s
- GraphQL operations and fragments must contain names
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/d8b8c1c040925d90.
Report an issue: GitHub.