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
- Give the eventType a valid dispatch target: either registrationName: 'onMyEvent' or phasedRegistrationNames: {captured: 'onMyEventCapture', bubbled: 'onMyEvent'}.
- Cross-check the field names against an existing plugin (e.g. ResponderEventPlugin) registered in the same registry.
- 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
- Give every eventType either registrationName or phasedRegistrationNames (captured/bubbled).
- Copy the field structure of a known plugin (e.g. ResponderEventPlugin) when writing new ones.
- Remember the registry validates at injection time - test injection in dev startup, not lazily.
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.