Repository navigation
ReactPlugin: malformed bundles may inherit previous plugin components #56169
Description
Apache Airflow version
3.1.0
If "Other Airflow 2 version" selected, which one?
No response
What happened?
Hi! I was reviewing the experimental react_app loading functionality while poking at the new release with some colleagues, and I might? have spotted a minor issue.
Looking at the plugin loading pattern, it appears that plugin authors are asked to assign their exports to globalThis.AirflowPlugin. Could an incorrectly bundled or empty plugin bundle inadvertently reuse the export of a prior plugin bundle?
Note: I haven't had time to create a minimal reproduction yet, and I'm still getting familiar with the code/bundling patterns used, so this might be completely off-base.
What you think should happen instead?
Maybe we could set globalThis.AirflowPlugin to undefined somewhere pre-import or post-assignment? (Minimial changeset, avoid this issue, identical plugin api)
I know this is literally brand new, and still experimental, so my focus is on confusion during prototyping/debugging and not on isolation between plugins. I'm sure there are many changes to come.
How to reproduce
Planned reproduction steps:
- Create a minimal
PluginAwith proper UMD bundle that exports toglobalThis.AirflowPlugin - Create a minimal
PluginBwith malformed/empty UMD bundle that does not setglobalThis.AirflowPlugin - Attempt to load
PluginAand thenPluginB - Check the value of
window['PluginB']
Operating System
TODO: update post-reproduction
Versions of Apache Airflow Providers
No response
Deployment
Other
Deployment details
TODO: update post-reproduction
Anything else?
Thanks for your time, and sorry if this turns out to be noise! Just wanted to get this written down while it was fresh in my mind so I could come back to it on monday.
Feel free to close or move to discussions if you think it's more appropriate.
Investigation notes
// Line ~40: Import bundle
import(/* @vite-ignore */ reactApp.bundle_url)
.then(() => {
// Line ~44: Check if plugin already cached
let pluginComponent = globalThis[reactApp.name];
// Line ~47: If not cached, grab from shared global
if (pluginComponent === undefined) {
pluginComponent = globalThis.AirflowPlugin; // ← ISSUE: could this be stale?
globalThis[reactApp.name] = pluginComponent;
}
// Line ~51: Validate and return
if (typeof pluginComponent !== "function") {
throw new TypeError(`Expected function, got ${typeof pluginComponent}`);
}
return { default: pluginComponent };
})Code references:
- dev/react-plugin-tools/react_plugin_template/
- src/main.tsx
export default PluginComponent
- vite.config.ts
- entry: src/main.tsx , format: umd, name: AirflowPlugin
- src/main.tsx
- airflow-core/src/airflow/ui/
- src/pages/ReactPlugin.tsx
- loading logic
- src/pages/ReactPlugin.tsx
Are you willing to submit PR?
- Yes I am willing to submit a PR!
Code of Conduct
- I agree to follow this project's Code of Conduct
Activity
boring-cyborg commentedon Sep 27, 2025
Thanks for opening your first issue here! Be sure to follow the issue template! If you are willing to raise PR to address this issue please do so, no need to wait for approval.
bbovenzi commentedon Oct 1, 2025
Good catch. I'll definitely look into this.