pmndrs/react-three-fiber · error · Error
Timed out after ${timeout}ms.
Error message
Timed out after ${timeout}ms. What it means
This error is thrown by react-three-test-renderer's waitFor helper when the provided callback still returns a falsy value after the configured timeout has elapsed. waitFor polls callback() (with an optional interval between checks) until it returns truthy, or null/undefined which is treated as 'stop waiting'. Its purpose is to fail fast on asynchronous rendering that never completes within the test's time budget.
Source
Thrown at packages/test-renderer/src/helpers/waitFor.ts:19
import { act } from 'react'
export interface WaitOptions {
interval?: number
timeout?: number
}
export async function waitFor(
callback: () => boolean | void,
{ interval = 50, timeout = 5000 }: WaitOptions = {},
): Promise<void> {
await act(async () => {
const start = performance.now()
while (true) {
const result = callback()
if (result || result == null) break
if (interval) await new Promise((resolve) => setTimeout(resolve, interval))
if (timeout && performance.now() - start >= timeout) throw new Error(`Timed out after ${timeout}ms.`)
}
})
}
View on GitHub (pinned to ff3899dbf4)
Solutions
- Verify the condition is actually reachable — run the same interaction/assertion synchronously to confirm what state waitFor is polling.
- If the awaited update legitimately takes longer (debounce, suspense load, animation frames), increase the timeout (and optionally interval) passed to waitFor.
- If a resource never resolves, mock the asset/data load (mock loader, fixture data) so the subtree finishes rendering.
- Ensure the callback returns something truthy on success; note that returning null/undefined makes waitFor exit immediately without error, so return an actual boolean/instance.
Example fix
// before
await tree.waitFor(() => tree.findAll(specificMesh).length > 0) // hangs -> Timed out after 5000ms.
// after
await tree.waitFor(() => tree.findAll(specificMesh).length > 0, { timeout: 10000, interval: 100 })
// or trigger the state that mounts the mesh before waiting:
act(() => { setIsReady(true) }) Defensive patterns
Strategy: retry
Validate before calling
// Make the condition self-evidently satisfiable before waiting
const found = tree.findAll(target)
if (found.length === 0 && !willEverMount(target)) {
// skip waitFor — nothing to wait for
} Type guard
null
Try / catch
try {
await tree.waitFor(() => tree.findAll(target).length > 0, { timeout: 5000, interval: 50 })
} catch (e) {
if (e instanceof Error && /Timed out after/.test(e.message)) {
// dump current tree to debug what actually rendered
console.log(JSON.stringify(tree.toTree(), null, 2))
} else throw e
} Prevention
- Set explicit, generous timeouts for asset-loading tests.
- Mock async loaders so waits are deterministic.
- Return a real boolean/instance from the callback (null/undefined exits immediately).
When it happens
Trigger: Calling waitFor(() => someCondition, { timeout }) where someCondition stays falsy: e.g. waiting for a mesh that never mounts, a Suspense resource that never resolves, an animation/state update that never happens, or passing a callback whose truthiness check is wrong (returns 0, '', false indefinitely).
Common situations: Async data fetching that hangs in tests (unmocked fetch/asset loader), components gated behind a state flag that the test never triggers, timeouts that are too short for a debounced/animated update, or awaiting an event that the renderer never fires because the scene is static.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- `${prefix} ${msg}`
- R3F: Hooks can only be used within the Canvas component!
- R3F: ${name} is not part of the THREE namespace! Did you for
- R3F: Primitives without 'object' are invalid!
- R3F: The args prop must be an array!
AI-assisted analysis of pmndrs/react-three-fiber@ff3899dbf4 (2026-08-28).
Data as JSON: /api/errors/c0e4707ebeedb543.
Report an issue: GitHub.