facebook/react · error · Error

233

233

Error message

Unsupported top level event type "${topLevelType}" dispatched

What it means

React Native's legacy event bridge resolves each incoming native top-level event name against the view-config-derived customBubblingEventTypes / customDirectEventTypes registries; a name present in neither throws at extractEvents time. Concretely, the native side dispatched an event that JS never registered because its ViewManager does not declare it.

Solutions

  1. Declare the event in the native ViewManager (e.g. the codegen events array with the exact top-prefixed name being dispatched) and rebuild the app.
  2. Verify the event name matches on both sides, case-sensitive, including the top prefix used for registration.
  3. If the target node may belong to another renderer, guard the dispatch on the native side - targetInst == null is ignored, but unregistered event names are not.

Example fix

// before - native view emits 'quantityChanged' but never declares it
// (RCTEventEmitter receiveEvent(..., 'quantityChanged', ...))

// after - declare it in the codegen spec so JS registers it
// NativeCounter.js
export default codegenNativeComponent('Counter', {
  interfaceName: 'RCTCounter',
  events: [{name: 'topQuantityChanged', bubbling: false}],
});
Defensive patterns

Strategy: validation

Validate before calling

import {UIManager} from 'react-native';
function isEventRegistered(viewName, eventName) {
  const viewConfig = UIManager.getViewManagerConfig(viewName);
  return Boolean(
    viewConfig?.Manager?.directEventTypes?.[eventName] ||
      viewConfig?.Manager?.bubblingEventTypes?.[eventName],
  );
}
if (!isEventRegistered('Counter', 'topQuantityChanged')) {
  throw new Error(
    'Declare topQuantityChanged on the native ViewManager before dispatching it',
  );
}

Prevention

When it happens

Trigger: A native view dispatches an event whose name is not declared in its ViewManager's event tables: a custom native component emitting an event without registering it (codegen directEvents/bubblingEvents), a casing or spelling mismatch between the native constant and the dispatched name, or an event sent for a node owned by another renderer.

Common situations: Custom native modules; adding a native event but not rebuilding/regenerating native code; Fabric codegen event-name differences between iOS and Android; typos in the 'top'-prefixed event constant.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/03deed5367632e0f. Report an issue: GitHub.

Appendix: source

Thrown at packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js:186

const ReactNativeBridgeEventPlugin: LegacyPluginModule<AnyNativeEvent> = {
  eventTypes: {} as EventTypes,

  extractEvents: function (
    topLevelType: TopLevelType,
    targetInst: null | Object,
    nativeEvent: AnyNativeEvent,
    nativeEventTarget: null | Object,
  ): ?Object {
    if (targetInst == null) {
      // Probably a node belonging to another renderer's tree.
      return null;
    }
    const bubbleDispatchConfig = customBubblingEventTypes[topLevelType];
    const directDispatchConfig = customDirectEventTypes[topLevelType];

    if (!bubbleDispatchConfig && !directDispatchConfig) {
      throw new Error(
        // $FlowFixMe[incompatible-type] - Flow doesn't like this string coercion because DOMTopLevelEventType is opaque
        `Unsupported top level event type "${topLevelType}" dispatched`,
      );
    }

    const event = SyntheticEvent.getPooled(
      bubbleDispatchConfig || directDispatchConfig,
      targetInst,
      nativeEvent,
      nativeEventTarget,
    );
    if (bubbleDispatchConfig) {
      const skipBubbling =
        event != null &&
        event.dispatchConfig.phasedRegistrationNames != null &&
        event.dispatchConfig.phasedRegistrationNames.skipBubbling;
      if (skipBubbling) {
        accumulateCapturePhaseDispatches(event);

View on GitHub (pinned to eafeac097b)