## Problem/Motivation

`\Drupal\file_uploader\Element\FileUploader::processFileUploader()` (line 72) and `\Drupal\file_uploader\Plugin\Field\FieldWidget\FileUploaderWidgetBase::massageFormValues()` (line 181) both treat `$element['#value']['fids']` (or `$values['fids']`) as always being an array of integer fids. They use it directly with `File::loadMultiple()` and `foreach`.

But the hidden `fids` form element stores its value as a **space-separated string** (built by `implode(' ', $fids)` in the same `processFileUploader` method, line 87). On form rebuild after POST, Drupal repopulates `$element['#value']['fids']` from this hidden field — meaning `$fids` is now a string like `"123"` or `"123 456"`, not an array.

Drupal 11's strict typing on `EntityBase::loadMultiple(?array $ids)` then throws TypeError, and `foreach` on a string in PHP 8 iterates per character — silently writing garbage entity references and producing the misleading entity validation error "This value should not be null".

## Steps to reproduce

1. Drupal 11.3.x + file_uploader 1.1.0 + a node bundle with a file field whose URI scheme is private:// (or any scheme — repro is independent of scheme).
2. Add a file widget using `file_uploader` (or `file_uploader_uppy`) on that bundle's form display.
3. Render the node-add form, upload a file via the widget — fid is created.
4. Submit the form (any save). Drupal rebuilds the form on the POST.
5. Observe one of:
- **WSOD** with `TypeError: Drupal\Core\Entity\EntityBase::loadMultiple(): Argument #1 ($ids) must be of type ?array, string given, called in .../file_uploader/src/Element/FileUploader.php on line 76`
- OR validation error **"This value should not be null"** on the file field, even though the file IS uploaded and the user can see it in the widget.

The error surfaces inconsistently because some code paths normalize `fids` to int[] (e.g. via valueCallback) before it reaches `processFileUploader`, but the rebuild path through `autosave_form` or the post-validate rebuild often does not.

## Proposed resolution

Coerce `$fids` to int[] at the top of both methods. Patch attached.

```diff
--- a/src/Element/FileUploader.php
+++ b/src/Element/FileUploader.php
@@ -70,7 +70,14 @@

// Prepare default values as existing files.
$values = [];
- if ($fids = $element['#value']['fids']) {
+ $fids = $element['#value']['fids'] ?? [];
+ if (is_string($fids)) {
+ $fids = array_filter(array_map('intval', preg_split('/\s+/', trim($fids))));
+ }
+ if (!is_array($fids)) {
+ $fids = [];
+ }
+ if (!empty($fids)) {
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $fileUrlGenerator */
$fileUrlGenerator = \Drupal::service('file_url_generator');
$files = File::loadMultiple($fids);
```

```diff
--- a/src/Plugin/Field/FieldWidget/FileUploaderWidgetBase.php
+++ b/src/Plugin/Field/FieldWidget/FileUploaderWidgetBase.php
@@ -179,7 +179,14 @@
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state): array {
$newValues = [];
- foreach ($values['fids'] as $fid) {
+ $fids = $values['fids'] ?? [];
+ if (is_string($fids)) {
+ $fids = array_filter(array_map('intval', preg_split('/\s+/', trim($fids))));
+ }
+ if (!is_array($fids)) {
+ $fids = [];
+ }
+ foreach ($fids as $fid) {
$newValues[] = ['target_id' => $fid];
}
return $newValues;
```

## Remaining tasks

- Add tests covering form rebuild scenarios (autosave_form, post-validate rebuilds).
- Consider a `valueCallback` on the FileUploader element that normalizes `fids` once at value-resolution time — would be a more architectural fix.

## User interface changes

None.

## API changes

None — both methods continue to accept the same call signature; they're now defensive against string input.

Comments

jaiap created an issue.