Problem/Motivation
ParagraphsType::getIconFile() assumes that if a file entity exists in the database for the given icon_uuid, the physical file also exists on disk. This is not always true when using remote file systems (S3FS with S3, Minio, GCS), where physical files can be lost independently of the database (bucket reset, storage migration, cache rebuild).
When this happens, getFileByUuid() returns the file entity, so restoreDefaultIcon() is never called. The generated icon URL points to a non-existent file, resulting in a permanent 404 with no self-healing path.
Current code (ParagraphsType.php, line 210):
$icon = $this->getFileByUuid($this->icon_uuid) ?: $this->restoreDefaultIcon();
Steps to reproduce
- Configure Drupal to use a remote file system for
public://(e.g., S3FS module) - Create or import paragraph types with icons (
icon_uuid+icon_defaultset in config) - Verify icons display correctly at
/admin/structure/paragraphs_type - Delete the icon files from the remote bucket without touching the Drupal database
- Clear caches (
drush cr) - Visit
/admin/structure/paragraphs_type - Result: Icons are broken (404).
restoreDefaultIcon()is never triggered.
Proposed resolution
Add a file_exists() check in getIconFile() to verify the physical file before trusting the file entity. If the physical file is missing, delete the orphan entity and let restoreDefaultIcon() recreate the file from icon_default base64 data.
Remaining tasks
- Review and test proposed patch
- Consider static caching the
file_exists()result to minimize remote I/O within a single request - Add test coverage for the "file entity exists, physical file missing" scenario
User interface changes
None. Icons that were previously showing as broken (404) will self-heal and display correctly.
API changes
None.
Data model changes
None.
Issue fork paragraphs-3592735
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
sjpagan commentedPushed a rebase on 8.x-1.x plus one commit.
Removed the $icon->delete() call. restoreDefaultIcon() already deletes the stale file entity and recreates it with the same uuid, so the getter no longer deletes anything. The check is now: if the file entity exists but the physical file does not, treat the icon as missing and let restoreDefaultIcon() run.
Added ParagraphsTypeIconTest with three cases: physical file present, physical file removed while the file entity stays, and no icon set.
I checked the test fails without the fix. With the original getIconFile() restored, testGetIconFileRestoresDefaultWhenPhysicalFileMissing fails on: Failed asserting that file "public://test_icon_missing-icon.png" exists.
The earlier pipeline failed on ParagraphsSummaryFormatterTest and MigrateUiParagraphsTest. The branch was 13 commits behind 8.x-1.x, including #3614739 and #3601382 which fix those tests. After the rebase phpunit passes.
phpcs reports two errors in ParagraphsType.php, at lines 20 and 150. Both are outside this change.