Image upload breaks after optional image without an image gets published

Created on 28 April 2025, 15 days ago

Overview

When publishing content with an optional image field that does not contain an image, subsequent image uploads to the field become non-functional.

Steps to reproduce

  1. Create content in Experience Builder that contains an optional image field
  2. Leave the optional image field empty (do not upload an image)
  3. Publish the content
  4. Refresh the page
  5. Attempt to upload an image to the image field
  6. Observe that image is not showing up in the preview and is lost after refreshing the page

Proposed resolution

User interface changes

🐛 Bug report
Status

Active

Version

0.0

Component

Page builder

Created by

🇫🇮Finland lauriii Finland

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @lauriii
  • After uploading an image getting 500 Internal Server Error :

    {
        "componentInstanceUuid": "03685066-c749-41d1-97f3-c2d50a4ae82a",
        "componentType": "js.image",
        "model": {
            "source": {
                "image": {
                    "sourceType": "default-relative-url",
                    "jsonSchema": {
                        "type": "object",
                        "properties": {
                            "src": {
                                "type": "string",
                                "format": "uri-reference",
                                "pattern": "^(/|https?://)?.*\\.(png|gif|jpg|jpeg|webp)(\\?.*)?(#.*)?$"
                            },
                            "alt": {
                                "type": "string"
                            },
                            "width": {
                                "type": "integer"
                            },
                            "height": {
                                "type": "integer"
                            }
                        },
                        "required": [
                            "src"
                        ]
                    },
                    "componentId": "js.image",
                    "value": "4"
                }
            },
            "resolved": {
                "image": "4"
            }
        }
    }
    
  • 🇬🇧United Kingdom longwave UK
  • 🇺🇸United States bnjmnm Ann Arbor, MI

    If I perform the steps as written, such as with the image-optional-with-example component from the xb_test_sdc module I can't reproduce the problem reported. Upload works fine.

    If I perform the steps per the gif in the issue summary, which appears to use the image sdc from Experience Builder's default components which has a required image, I do run into problems such as being able to publish despite a missing required field, and the preview is then completely empty which is different from what was reported but perhaps the same underlying issue.

    Top of Image SDC defintion

    $schema: https://git.drupalcode.org/project/drupal/-/raw/HEAD/core/assets/schemas/v1/metadata.schema.json
    name: Image
    props:
      type: object
      required:
        - image
    
  • 🇫🇮Finland lauriii Finland

    Ah, this is specific to required image fields! Updating the title + issue summary.

  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

    I bet this is where @longwave's recently added \Drupal\Tests\experience_builder\Kernel\ComponentInputsFormTest (since #3518253) would come in handy in writing a failing test.

    Root cause not yet clear though.

  • 🇪🇸Spain isholgueras

    I've identified difference between both behaviors.
    The error is a 500 error because>

        "message": "assert(self::isUrlJsonSchema($this-\u003EjsonSchema))",
        "file": "\/var\/www\/html\/web\/modules\/contrib\/experience_builder\/src\/PropSource\/DefaultRelativeUrlPropSource.php",
        "line": 111,
    

    The problem is because when you add the component and add the image, the "model" values that comes in the request are:

          'model' => [
            'source' => [
              'image' => [
                'expression' => 'ℹ︎entity_reference␟{src↝entity␜␜entity:media:image␝field_media_image␞␟entity␜␜entity:file␝uri␞␟url,alt↝entity␜␜entity:media:image␝field_media_image␞␟alt,width↝entity␜␜entity:media:image␝field_media_image␞␟width,height↝entity␜␜entity:media:image␝field_media_image␞␟height}',
                'sourceType' => 'static:field_item:entity_reference',
                'value' => '1',
    ...
    

    (There is no jsonSchema but the expression of the media entity_reference)

    But if you follow the steps to reproduce, the model values that comes in the request are:

          'model' => [
            'source' => [
              'image' => [
                'sourceType' => 'default-relative-url',
                'jsonSchema' => [
                  'type' => 'object',
                  'properties' => [
                    'src' =>
                      [
                        'type' => 'string',
                        'format' => 'uri-reference',
                        'pattern' => '^(/|https?://)?.*\\.(png|gif|jpg|jpeg|webp)(\\?.*)?(#.*)?$',
                      ],
    ...
    

    Here, there is a jsonSchema of type default-relative-url, and it fails because the value 1 (the image with media id 1) is not a valid UrlJsonSchema.

    I'll keep digging in.

  • 🇺🇸United States bnjmnm Ann Arbor, MI

    Now that the issue summary has been updated and matches that I took to reproduce the same symptoms, pay special attention to the steps I've bolded

    1. Create content in Experience Builder that contains a required image field
    2. Leave the image field empty (do not upload an image)
    3. Publish the content
    4. Refresh the page
    5. Attempt to upload an image to the image field
    6. Observe that image is not showing up in the preview and is lost after refreshing the page

    In other words, this is a problem that only occurs when we publish content that has an empty required field, which IMO shouldn't be possible in the first place. For simple-shaped fields, client side validation already prevents empty values from being written to autosave, so the publish process doesn't have to enforce it in those instances (but presumably can as it uses validation constraints). For fields such as image with more complex shapes, it looks like there might be more needed to enforce them being required.

  • 🇫🇮Finland lauriii Finland

    In other words, this is a problem that only occurs when we publish content that has an empty required field, which IMO shouldn't be possible in the first place. For simple-shaped fields, client side validation already prevents empty values from being written to autosave, so the publish process doesn't have to enforce it in those instances (but presumably can as it uses validation constraints). For fields such as image with more complex shapes, it looks like there might be more needed to enforce them being required.

    It must be possible. The whole premise of XB is that all required fields must have an example value. What that example value enables us to do is to render previews but to guarantee that when you add a component to a page, it's in a state where it could be saved.

    The difference between optional and required image fields in XB is that from an optional image field, it must be possible to remove that image and not render an image at all. For required image fields, it should never be possible to remove the image completely, but it should be possible to replace it with another image.

  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

    AFAICT this is what you intended to do, @isholgueras?

  • 🇬🇧United Kingdom longwave UK

    Thanks for adding the test coverage, I removed some unused arrays so it's just the minimal thing that actually needs to be tested here, back to Wim for review.

Production build 0.71.5 2024