#37364·elementor

Editor V4: Atomic Form File Upload inside a Component silently discards the uploaded file (4.3.0-beta3)

Author: st-ephCreated Sep 21, 2026Updated Sep 21, 2026

Description

Elementor 4.3.0-beta3 + Elementor Pro 4.3.0-beta3, Editor V4, Atomic Form inside a Component.

When an Atomic Form lives inside a Component, a file chosen in its File Upload field is silently discarded on submit:

  • the submission is reported as successful to the visitor and to every action (collect-submissions success, email success);
  • the stored submission has an empty value for the File Upload field;
  • the email has no link and no attachment, whichever "Send file" mode is chosen (link / attach / both);
  • wp-content/uploads/elementor/forms/ is never created and the temp upload is dropped;
  • nothing is logged, nothing is returned in the AJAX response. The only trace is the missing file.

If the File Upload field is marked Required, the visitor instead gets "This field is required." even though they did pick a file, so the form cannot be submitted at all.

The same form placed directly on a page (not inside a Component) uploads the file correctly.

This is a second, data-losing symptom of the id mismatch described in #37322 (merge tags resolving empty). That report covers build_cssid_map(); this one goes through a different code path, File_Upload_Handler, and is not mentioned there, so I am filing it separately so it gets tracked.

Root cause (beta3 source)

Each field inside a Component instance is rendered with two ids: data-id = instance-scoped id (regenerated per instance by the Components module, original kept in origin_id), data-interaction-id = the component's original element id.

  1. elementor/assets/js/atomic-widgets-form-handler.js posts the origin id for every field:
    javascript
    const allInputs = [...form.querySelectorAll(ATOMIC_FORM_FIELD_SELECTOR)].filter((input) => input.dataset.interactionId);
    const id = input.dataset.interactionId;   // -> form_fields[i][id]
  2. elementor-pro/modules/atomic-form/file-upload/field-settings-collector.php walks the expanded form element and keys the file field settings by the instance id:
    php
    $map[ $element['id'] ] = Render_Props_Resolver::for_settings()->resolve( File_Upload::get_props_schema(), $element['settings'] ?? [] );
  3. File_Upload_Handler::process() builds index -> posted id from form_fields, groups $_FILES by that posted (origin) id, then loops over the settings map (instance ids):
    php
    $field_files = $files_by_element_id[ $element_id ] ?? [];
    $validation_error = ( new Composite_Validator( $settings ) )->validate( $field_files );
    ...
    if ( empty( $field_files ) ) { continue; }
    The two id sets never intersect, so $field_files is always empty: the file is never validated, never stored, never passed to $context['files'], and form_data_overrides stays empty. With required on, Required_Validator returns "This field is required." for the same reason.

Measured on one form (Contact form inside a Component):

id
File Upload element in the expanded tree (Field_Settings_Collector key) 1n678wt
origin_id on that element / data-interaction-id in the markup / id posted by the browser 71a1255d

Submission recorded by collect-submissions for that request (values table):

4e83b91  Bugtest
1e5f159b FileDrop
3272abdf [email protected]
2581923a Triangle
71a1255d            <- File Upload: empty, a 193-byte PDF was attached
27e01f63 Yes

AJAX response for the same request:

json
{"success":true,"data":{"message":"Your submission was successful.","data":{"actionResults":[{"type":"collect-submissions","status":"success","message":"Submission saved successfully","submissionId":6},{"type":"email","status":"success","message":"Email sent successfully"}],"allActionsSucceeded":true,"failedActions":[]}}}

uploads/elementor/forms/ does not exist on the site after the submission.

Suggested fix: use one id on both sides. Either post data-id for the fields (the handler already uses form.dataset.id for the form itself), or have Field_Settings_Collector (and build_cssid_map()) key by origin_id when present, or remap posted ids via origin_id before processing. Whichever way, a file that was posted but matched no field should probably produce an error rather than a silent success.

Steps to reproduce

  1. Clean WordPress install, Hello Elementor 3.5.1, Elementor 4.3.0-beta3, Elementor Pro 4.3.0-beta3. Editor V4, Atomic Widgets and Atomic Form experiments active.
  2. In Editor V4 create an Atomic Form with a Text field, an Email field and a File Upload field (leave Required off, allowed types default, "Send file" = Email with link). Actions after submit: Collect Submissions + Email.
  3. Save the form as a Component and place the Component on a page. Publish.
  4. Open the page on the front end, fill in the fields, pick a small PDF or PNG in the File Upload field, submit.
  5. Observe the success message. Open Elementor > Submissions and the received email.
  6. Check wp-content/uploads/elementor/forms/.
  7. Control: put the same form directly on a page (not via the Component) and repeat steps 4 to 6.

Optional: DevTools > Network > admin-ajax.php shows form_fields[n][id] equal to the field's data-interaction-id, and form_fields[n][value][0] carrying the file.

Observed (inside Component): submission and email report success, the File Upload value is empty, the email has no link/attachment, uploads/elementor/forms/ is not created. Set the field to Required and the same submission is rejected with "This field is required.".

Observed (direct on page): file stored under uploads/elementor/forms/, link in the submission and email.

Expected behavior

A file chosen in a File Upload field is validated, stored and delivered (link or attachment) the same way whether the form is placed on the page directly or through a Component. If for any reason a posted file cannot be matched to a field, the submission should fail with an error instead of reporting success and dropping the file.

Elementor System Info

txt
== Server Environment ==
	Operating System: Linux
	Software: nginx/1.31.3
	MySQL version: mariadb.org binary distribution v11.4.12-MariaDB-ubu2404
	PHP Version: 8.4.21
	PHP Memory Limit: 1024M
	PHP Max Input Vars: 10000
	PHP Max Post Size: 128M
	GD Installed: Yes
	ZIP Installed: Yes
	Write Permissions: All right
	Elementor Library: Connected

== WordPress Environment ==
	Version: 7.1.1
	Site URL: [redacted]
	Home URL: [redacted]
	WP Multisite: No
	Max Upload Size: 128 MB
	Memory limit: 512M
	Max Memory limit: 1024M
	Permalink Structure: /%postname%/
	Language: [redacted]
	Timezone: [redacted]
	Admin Email: [redacted]
	Debug Mode: Inactive

== Theme ==
	Name: Hello Elementor
	Version: 3.5.1
	Author: Elementor Team
	Child Theme: No

== User ==
	Role: administrator
	WP Profile lang: [redacted]
	User Agent: [redacted]

== Active Plugins ==
	Advanced Custom Fields PRO
		Version: 6.8.10
		Author: WP Engine

	Easy WP SMTP
		Version: 2.15.1
		Author: Easy WP SMTP

	Elementor
		Version: 4.3.0-beta3
		Author: Elementor.com

	Elementor Pro
		Version: 4.3.0-beta3
		Author: Elementor.com

	EWWW Image Optimizer
		Version: 8.7.7
		Author: Exactly WWW

	Rank Math SEO
		Version: 1.0.278
		Author: Rank Math SEO

	Rank Math SEO PRO
		Version: 3.0.121
		Author: Rank Math SEO

	Wordfence Security
		Version: 9.0.1
		Author: Wordfence


== Must-Use Plugins ==
	None

== Elements Usage ==
	
	archive : 1
		e-component : 3
		e-flexbox : 4
		e-paragraph : 1
	footer : 1
		e-component : 1
		e-flexbox : 1
	header : 1
		container : 5
		e-button : 1
		e-component : 2
		e-div-block : 1
		e-flexbox : 5
		e-image : 1
		e-paragraph : 1
		e-svg : 2
		mega-menu : 1
	page : 1
		e-component : 1
		e-flexbox : 1
	popup : 2
		e-component : 3
		e-flexbox : 3
	wp-post : 0
		e-button : 3
		e-component : 13
		e-div-block : 3
		e-divider : 2
		e-flexbox : 22
		e-grid : 3
		e-heading : 4
		e-paragraph : 18
		e-svg : 9
	wp-page : 9
		e-accordion : 5
		e-accordion-item : 15
		e-accordion-item-content : 15
		e-accordion-item-header : 15
		e-accordion-item-icon : 3
		e-accordion-item-title : 15
		e-button : 27
		e-collection-loop : 1
		e-collection-loop-item : 1
		e-collection-loop-layout : 1
		e-component : 31
		e-divider : 10
		e-flexbox : 140
		e-grid : 11
		e-heading : 105
		e-paragraph : 174
		e-self-hosted-video : 1
		e-svg : 39
		e-tab : 15
		e-tab-content : 15
		e-tabs : 5
		e-tabs-content-area : 5
		e-tabs-menu : 5
		e-youtube : 1
	single-post : 1
		e-component : 3
		e-flexbox : 4
		e-paragraph : 1
		theme-post-content : 1
	error-404 : 1
		e-component : 1
		e-flexbox : 1


== Settings ==
	
	cpt_support: post, page
	disable_color_schemes: yes
	disable_typography_schemes: yes
	allow_tracking: no
	unfiltered_files_upload: 1
	font_display: swap
	meta_generator_tag: 1
	disabled_elements: inner-section, heading, image, video, button, divider, spacer, image-box, google_maps, icon-box, image-carousel, image-gallery, counter, progress, testimonial, alert, audio, shortcode, menu-anchor, sidebar, read-more, rating, wp-widget-pages, wp-widget-calendar, wp-widget-archives, wp-widget-media_audio, wp-widget-media_image, wp-widget-media_gallery, wp-widget-media_video, wp-widget-meta, wp-widget-search, wp-widget-text, wp-widget-categories, wp-widget-recent-posts, wp-widget-recent-comments, wp-widget-rss, wp-widget-tag_cloud, wp-widget-nav_menu, wp-widget-custom_html, wp-widget-block, text-path, link-in-bio, theme-site-logo, theme-site-title, theme-page-title, theme-post-title, theme-post-excerpt, theme-post-featured-image, theme-archive-title, archive-posts, off-canvas, posts, portfolio, gallery, login, slides, animated-headline, hotspot, price-list, price-table, flip-box, call-to-action, media-carousel, testimonial-carousel, reviews, table-of-contents, countdown, share-buttons, author-box, post-comments, post-navigation, post-info, sitemap, blockquote, facebook-embed, facebook-page, lottie, code-highlight, video-playlist, paypal-button, progress-tracker, nested-carousel, link-in-bio-var-2, link-in-bio-var-3, link-in-bio-var-5, link-in-bio-var-6, link-in-bio-var-7, link-in-bio-var-4, form, nested-tabs, html, breadcrumbs, loop-grid, taxonomy-filter, template, stripe-button, social-icons, nav-menu, icon, icon-list, nested-accordion, loop-carousel


== Features ==
	Custom Fonts: 2
	Custom Icons: 0

== Integrations ==
	


== Elementor Experiments ==
	Inline Font Icons: Active by default
	Additional Custom Breakpoints: Active by default
	Container: Active by default
	Optimized Markup: Active by default
	Optimized CSS Files: Inactive by default
	Default to New Theme Builder: Active by default
	Header & Footer: Inactive
	Nested Elements: Active by default
	Pages Panel: Inactive by default
	Atomic Widgets: Active by default
	List: Inactive by default
	Icon Button: Inactive by default
	Accordion: Inactive by default
	SVG Library: Inactive by default
	Launchpad Checklist: Inactive by default
	Page Audit: Inactive by default
	Editor V4: Active by default
	Component Variants: Inactive by default
	Angie in editor panels: Inactive by default
	Generate website markdown: Inactive by default
	Agents llms.txt: Inactive by default
	Dynamic Assets Management: Inactive by default
	Import/Export Customization: Active by default
	Atomic Form: Active by default
	Loop: Active by default
	Menu: Active by default
	In-Editor Feedback: Inactive by default
	Elementor Editor Events: Active by default


== Log ==
	
PHP: showing 4 of 4
PHP: 2026-09-12 18:09:27 [warning X 30][[path]/plugins/elementor/modules/interactions/interactions-frontend-handler.php::61] foreach() argument must be of type array|object, null given
PHP: 2026-09-12 18:21:08 [notice X 150][[path]/plugins/elementor/modules/global-classes/atomic-global-styles.php::410] Elementor\Modules\GlobalClasses\Atomic_Global_Styles::get_cache_root_key(): Implicitly marking parameter $key as nullable is deprecated, the explicit nullable type must be used instead
PHP: 2026-09-12 18:53:21 [warning X 8][[path]/plugins/elementor/modules/history/revisions-manager.php::363] Undefined array key "editor_post_id"
PHP: 2026-09-18 15:04:44 [error X 1][[path]/plugins/elementor/core/settings/manager.php::149] Uncaught Error: Call to a member function get_tabs_controls() on null in [path]/plugins/elementor/core/settings/manager.php:149

JS: showing 3 of 3
JS: 2026-09-12 08:38:36 [error X 91][elementor-pro/assets/js/editor.min.js?ver=4.3.0-beta2:2:103668] Cannot read properties of undefined (reading 'find')
JS: 2026-09-12 08:38:49 [error X 30][elementor/assets/js/editor.min.js?ver=4.3.0-beta2:9:22126] Failed to execute 'querySelector' on 'Document': '#' is not a valid selector.
JS: 2026-09-17 02:37:00 [error X 27][elementor/assets/js/editor-modules.min.js?ver=4.3.0-beta2:6:9016] Cannot read properties of null (reading 'editor')

Log: showing 10 of 10
2026-09-20 10:19:07 [info] Elementor data updater process has been queued. (Elementor 4.3.0-beta2 -> 4.3.0-beta3)
2026-09-20 10:20:16 [info] elementor::elementor_updater Started
2026-09-20 10:20:16 [info] Elementor/Upgrades - _on_each_version Start
2026-09-20 10:20:16 [info] Elementor/Upgrades - _on_each_version Finished
2026-09-20 10:20:16 [info] Elementor data updater process has been completed. (Elementor 4.3.0-beta2 -> 4.3.0-beta3)
2026-09-20 12:02:35 [info] Elementor data updater process has been queued. (Elementor Pro 4.3.0-beta2 -> 4.3.0-beta3)
2026-09-20 12:04:00 [info] elementor-pro::elementor_pro_updater Started
2026-09-20 12:04:00 [info] Elementor Pro/Upgrades - _on_each_version Start
2026-09-20 12:04:00 [info] Elementor Pro/Upgrades - _on_each_version Finished
2026-09-20 12:04:00 [info] Elementor data updater process has been completed. (Elementor Pro 4.3.0-beta2 -> 4.3.0-beta3)

== Elementor - Compatibility Tag ==
	
	Elementor Pro: Compatibility not specified

== Elementor Pro - Compatibility Tag ==
	

Agreement

  • I confirm I have read and followed all the guidelines and instructions outlined in the Editor V4 Bug Report form.
  • I agree that my issue may be closed without further action if it doesn't meet all the requirements outlined in the Editor V4 Bug Report form.