In WordPress, a media file is not a file: it is a record. The content type is called “attachment”, the file path lives in the _wp_attached_file meta, and the derivatives generated at upload live in the sizes array of _wp_attachment_metadata. The whole responsive image chain starts there: wp_get_attachment_image_srcset calls wp_get_attachment_image_src, which calls wp_get_attachment_metadata. Every link needs the attachment id, and returns false without it. A DAM, for its part, indexes by its own identifier and serves derivative URLs: the two repositories never meet on their own.
Elementor does not write its content into post_content: it serialises the page’s entire tree as JSON, in the _elementor_data meta. Its media control natively stores the url, id, size triple there — so the slot for an identifier already exists. At render time, Group_Control_Image_Size does not test that identifier alone: it also checks that the requested size is one of the sizes WordPress has registered. With both, Elementor hands over to wp_get_attachment_image and adds the wp-image class followed by the number. With either missing — and the “Custom” size the panel offers is not a registered size — it composes an img tag itself with src, alt and lazy loading, nothing more. The identifier is necessary, not sufficient.
WordPress’s own safety net catches nothing. On a page that goes through the_content, WordPress re-processes the content with wp_filter_content_tags, hooked at priority 12, therefore after Elementor’s insertion at priority 9: the HTML does pass through the net. But that net looks for the identifier inside the wp-image class, by regular expression — precisely the class the no-id branch never writes. The net exists, runs at the right moment, and finds nothing to hook onto. So the question is not which URL to serve, but whether the identifier survives to render.