facebook/react · error · Error

98

98

Error message

EventPluginRegistry: Failed to publish event `${eventName}` for plugin `${pluginName}`.

What it means

When publishing a plugin's eventTypes, publishEventForPlugin only succeeds if the dispatchConfig declares phasedRegistrationNames or a registrationName - otherwise the registry throws 'Failed to publish event ... for plugin ...' because the event could never be dispatched to a listener prop. (A duplicate event name would instead throw the separate 'More than one plugin' error.)

Source

Thrown at packages/react-native-renderer/src/legacy-events/EventPluginRegistry.js:76

    if (!pluginModule.extractEvents) {
      throw new Error(
        'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +
          `method, but \`${pluginName}\` does not.`,
      );
    }

    plugins[pluginIndex] = pluginModule;
    const publishedEvents = pluginModule.eventTypes;
    for (const eventName in publishedEvents) {
      if (
        !publishEventForPlugin(
          publishedEvents[eventName],
          pluginModule,
          eventName,
        )
      ) {
        throw new Error(
          `EventPluginRegistry: Failed to publish event \`${eventName}\` for plugin \`${pluginName}\`.`,
        );
      }
    }
  }
}

/**
 * Publishes an event so that it can be dispatched by the supplied plugin.
 *
 * @param {object} dispatchConfig Dispatch configuration for the event.
 * @param {object} PluginModule Plugin publishing the event.
 * @return {boolean} True if the event was successfully published.
 * @private
 */
function publishEventForPlugin(
  dispatchConfig: DispatchConfig,
  pluginModule: LegacyPluginModule<AnyNativeEvent>,

View on GitHub (pinned to eafeac097b)

Solutions

  1. Give the eventType a valid dispatch target: either registrationName: 'onMyEvent' or phasedRegistrationNames: {captured: 'onMyEventCapture', bubbled: 'onMyEvent'}.
  2. Cross-check the field names against an existing plugin (e.g. ResponderEventPlugin) registered in the same registry.
  3. Re-inject after fixing - the registry validates at injection time, so errors surface immediately.

Example fix

// before
MyPlugin.eventTypes = {
  MyGesture: {dependencies: ['topTouchStart']},
};

// after
MyPlugin.eventTypes = {
  MyGesture: {
    phasedRegistrationNames: {
      captured: 'onMyGestureCapture',
      bubbled: 'onMyGesture',
    },
    dependencies: ['topTouchStart'],
  },
};
Defensive patterns

Strategy: validation

Validate before calling

const hasDispatchTarget = config =>
  Boolean(config.phasedRegistrationNames || config.registrationName);
for (const [eventName, config] of Object.entries(MyPlugin.eventTypes)) {
  if (!hasDispatchTarget(config)) {
    throw new Error(
      `eventType '${eventName}' needs phasedRegistrationNames or registrationName`,
    );
  }
}
injectEventPluginsByName({MyPlugin});

Type guard

const isValidDispatchConfig = config =>
  config != null &&
  Boolean(config.phasedRegistrationNames || config.registrationName);

Prevention

When it happens

Trigger: An injected plugin whose eventTypes entry has neither phasedRegistrationNames (captured/bubbled) nor registrationName - e.g. a hand-written or incompletely ported eventType config with only dependencies or custom fields.

Common situations: Hand-written plugin event configs; plugins copied from a different event system that uses different field names (e.g. only a name field); refactors that drop the registration fields.

Related errors


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