facebook/react · error · Error
191
191
Error message
Invalid argument passed as callback. Expected a function. Instead received: ${callback} What it means
callCallback() invokes the second argument of a class component's setState()/forceUpdate() after the update commits. React stores that argument in the update queue and runs it through callCallback, which hard-asserts it is a function. The message echoes the invalid value, so 'received: [object Object]' means an object was passed where the callback belongs.
Source
Thrown at packages/react-reconciler/src/ReactFiberClassUpdateQueue.js:702
// This should be fine because the only two other things that contribute to
// expiration time are props and context. We're already in the middle of the
// begin phase by the time we start processing the queue, so we've already
// dealt with the props. Context in components that specify
// shouldComponentUpdate is tricky; but we'll have to account for
// that regardless.
markSkippedUpdateLanes(newLanes);
workInProgress.lanes = newLanes;
workInProgress.memoizedState = newState;
}
if (__DEV__) {
currentlyProcessingQueue = null;
}
}
function callCallback(callback: () => mixed, context: any) {
if (typeof callback !== 'function') {
throw new Error(
'Invalid argument passed as callback. Expected a function. Instead ' +
`received: ${callback}`,
);
}
callback.call(context);
}
export function resetHasForceUpdateBeforeProcessing() {
hasForceUpdate = false;
}
export function checkHasForceUpdateAfterProcessing(): boolean {
return hasForceUpdate;
}
export function deferHiddenCallbacks<State>(
updateQueue: UpdateQueue<State>,View on GitHub (pinned to eafeac097b)
Solutions
- Make the second argument a function or omit it: this.setState(next, () => { /* ... */ })
- Move options-object logic out of the second argument into the callback or componentDidUpdate
- Log typeof of the variable passed as callback to find the bad call site
- Library authors: validate callbacks from external callers before enqueueing updates
Example fix
// before
this.setState({count: next}, {onDone: this.finish});
// after
this.setState({count: next}, this.finish); Defensive patterns
Strategy: type-guard
Validate before calling
safeSetState(partial, cb) {
if (cb !== undefined && typeof cb !== 'function') {
throw new TypeError(`setState callback must be a function, got ${typeof cb}`);
}
this.setState(partial, cb);
} Type guard
const isCallback = (cb) => cb == null || typeof cb === 'function';
Try / catch
The throw surfaces while processing the update; an ErrorBoundary near the component catches it. Fix the call site - the same invalid callback re-throws on every retry.
Prevention
- Type setState as setState<S>(state: S | ((prev: S) => S), callback?: () => void)
- Never pass an options object as the second setState argument
- Prefer componentDidUpdate over setState callbacks in new code
When it happens
Trigger: this.setState({x: 1}, someNonFunction) - typically an options object, a string, or a function invoked instead of passed (this.setState(s, cb())); this.forceUpdate('x'); third-party code calling enqueueSetState(instance, state, callback) with a malformed callback.
Common situations: Porting callback-style APIs from other frameworks; passing a useEffect-style options object as the second setState argument; refactors from promise-style setState(...).then(...) patterns; typos where a callback name is passed as a string.
Related errors
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/ae14a8563a29a302.
Report an issue: GitHub.