nopSolutions/nopCommerce · error · Exception

Picture cannot be loaded

Error message

Picture cannot be loaded

What it means

Thrown while preparing the admin product-picture grid (PrepareProductPictureListModel). For each ProductPicture mapping row the factory loads the linked Picture via _pictureService.GetPictureByIdAsync(productPicture.PictureId); if that returns null it throws a bare System.Exception (not NopException), meaning there is no local catch specific to this — it propagates up the request pipeline. It indicates the join table ProductPicture references a PictureId that no longer has a Picture record.

Source

Thrown at src/Presentation/Nop.Web/Areas/Admin/Factories/ProductModelFactory.cs:1589

    public virtual async Task<ProductPictureListModel> PrepareProductPictureListModelAsync(ProductPictureSearchModel searchModel, Product product)
    {
        ArgumentNullException.ThrowIfNull(searchModel);
        ArgumentNullException.ThrowIfNull(product);

        //get product pictures
        var productPictures = (await _productService.GetProductPicturesByProductIdAsync(product.Id)).ToPagedList(searchModel);

        //prepare grid model
        var model = await new ProductPictureListModel().PrepareToGridAsync(searchModel, productPictures, () =>
        {
            return productPictures.SelectAwait(async productPicture =>
            {
                //fill in model values from the entity
                var productPictureModel = productPicture.ToModel<ProductPictureModel>();

                //fill in additional values (not existing in the entity)
                var picture = (await _pictureService.GetPictureByIdAsync(productPicture.PictureId))
                    ?? throw new Exception("Picture cannot be loaded");

                productPictureModel.PictureUrl = (await _pictureService.GetPictureUrlAsync(picture)).Url;

                productPictureModel.OverrideAltAttribute = picture.AltAttribute;
                productPictureModel.OverrideTitleAttribute = picture.TitleAttribute;

                return productPictureModel;
            });
        });

        return model;
    }

    /// <summary>
    /// Prepare paged product video list model
    /// </summary>
    /// <param name="searchModel">Product video search model</param>
    /// <param name="product">Product</param>

View on GitHub (pinned to 64bdf2ff08)

Solutions

  1. Delete or repair the orphaned ProductPictureMapping rows whose PictureId has no matching Picture row.
  2. Re-upload the missing picture for the affected product and relink it.
  3. Run an integrity check: SELECT * FROM Product_Picture_Mapping ppm LEFT JOIN Picture p ON p.Id = ppm.PictureId WHERE p.Id IS NULL; then remove those mappings.
  4. Restore the Picture table from backup if multiple pictures are missing.

Example fix

// before
var picture = (await _pictureService.GetPictureByIdAsync(productPicture.PictureId))
    ?? throw new Exception("Picture cannot be loaded");
// after (skip the broken mapping row instead of failing the whole grid)
var picture = await _pictureService.GetPictureByIdAsync(productPicture.PictureId);
if (picture is null)
{
    await _logger.WarningAsync($"ProductPicture mapping {productPicture.Id} references missing picture {productPicture.PictureId}");
    return null;
}
Defensive patterns

Strategy: validation

Validate before calling

// Before rendering the grid, confirm every mapping has a picture.
var productPictures = await _productService.GetProductPicturesByProductIdAsync(product.Id);
var orphans = productPictures.Where(pp => (await _pictureService.GetPictureByIdAsync(pp.PictureId)) is null).ToList();
if (orphans.Any())
    await _logger.WarningAsync($"{orphans.Count} product-picture mappings reference missing pictures.");

Type guard

static bool PictureResolved(Picture p) => p is not null;

Try / catch

// This throws a bare System.Exception with no local catch in the lambda —
// wrap the grid preparation so a single broken row does not fail the page.
try { /* PrepareToGridAsync body */ }
catch (Exception ex) when (ex.Message == "Picture cannot be loaded")
{
    await _logger.WarningAsync(ex.Message, ex);
    /* return empty/partial grid */
}

Prevention

When it happens

Trigger: A ProductPictureMapping row has a PictureId for which PictureService.GetPictureByIdAsync returns null. Typically a picture was hard-deleted (or its DB row removed) while the product-picture mapping row was left behind, or a failed picture upload left an orphan mapping.

Common situations: Manual DB cleanup of the Picture table without cleaning ProductPictureMapping; partial import/restore where picture binary rows are missing; storage migration that dropped picture records; concurrent deletion race during image management.

Related errors


AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13). Data as JSON: /api/errors/120613c7002f3f11. Report an issue: GitHub.