facebook/react · error · Error
284
284
Error message
Expected ref to be a function, an object returned by React.createRef(), or undefined/null.
What it means
markRef() validates every ref attached to a fiber before scheduling the Ref effect. React accepts exactly three ref shapes: a function (callback ref), a ref object (from useRef()/React.createRef()), or null/undefined. Any primitive - a string, number, boolean - throws during beginWork, before the element can render.
Source
Thrown at packages/react-reconciler/src/ReactFiberBeginWork.js:1422
}
}
const nextProps: ProfilerProps = workInProgress.pendingProps;
const nextChildren = nextProps.children;
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
function markRef(current: Fiber | null, workInProgress: Fiber) {
// TODO: Check props.ref instead of fiber.ref when enableRefAsProp is on.
const ref = workInProgress.ref;
if (ref === null) {
if (current !== null && current.ref !== null) {
// Schedule a Ref effect
workInProgress.flags |= Ref | RefStatic;
}
} else {
if (typeof ref !== 'function' && typeof ref !== 'object') {
throw new Error(
'Expected ref to be a function, an object returned by React.createRef(), or undefined/null.',
);
}
if (current === null || current.ref !== ref) {
// Schedule a Ref effect
workInProgress.flags |= Ref | RefStatic;
}
}
}
function mountIncompleteFunctionComponent(
_current: null | Fiber,
workInProgress: Fiber,
Component: any,
nextProps: any,
renderLanes: Lanes,
) {
resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);View on GitHub (pinned to eafeac097b)
Solutions
- Locate the offending element via the error's component stack and inspect its ref value
- Replace string or primitive refs with an object ref (useRef/createRef) or a callback ref
- If the ref arrives via a spread object, strip the ref key before spreading or set ref to a valid value
- Type the prop as React.Ref<T> so the compiler rejects primitives
Example fix
// before
<input ref="username" />;
// after
function Form() {
const username = useRef(null);
return <input ref={username} />;
} Defensive patterns
Strategy: type-guard
Validate before calling
function safeProps(props) {
if ('ref' in props && !isValidRef(props.ref)) {
throw new TypeError(`Invalid ref of type ${typeof props.ref}: ${String(props.ref)}`);
}
return props;
} Type guard
function isValidRef(ref) {
return (
ref == null ||
typeof ref === 'function' ||
(typeof ref === 'object' && 'current' in ref)
);
} Try / catch
Render-phase throw: an ErrorBoundary above the element catches it. Log the component stack, then fix the ref value at its source - retrying without the fix re-throws identically.
Prevention
- Never use string refs - modern React does not support them
- Type ref props as React.Ref<T>, never any
- Strip stray ref keys from dynamically built props objects before spreading
When it happens
Trigger: Passing a legacy string ref (ref="input"); passing a computed value that is a primitive (ref={someString}, ref={0}, ref={true}); spreading a props object that contains a stray ref key; forwarding a non-ref value through React.forwardRef.
Common situations: Codebases migrating off removed string-ref behavior; dynamically built props objects where a ref key leaks in from data; refactors where a callback ref loses its function wrapper; TypeScript code that bypasses ref types with any.
Related errors
- 379
- Bridge event names must be non-empty strings.
- Can only set one of `children` or `props.dangerouslySetInner
- `props.dangerouslySetInnerHTML` must be in the form `{__html
- Invalid tag: ${tag}
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/63aad8b5f95150f1.
Report an issue: GitHub.