{"record":{"id":"ecac2f9d885d1096","repo":"getgrav/grav","slug":"bad-form-data-for-field-collection-s-s-used-i","errorCode":null,"errorMessage":"Bad form data for field collection '%s': %s used instead of an array","messagePattern":"Bad form data for field collection '(.+?)': (.+?) used instead of an array","errorType":"validation","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Common/Data/BlueprintSchema.php","lineNumber":370,"sourceCode":"                // TODO: Add support to collections.\n                continue;\n            }\n            if (is_array($value)) {\n                // Special toggle handling for all the nested data.\n                $toggle = $toggles[$key] ?? [];\n                if (!is_array($toggle)) {\n                    if (!$toggle) {\n                        $data[$key] = null;\n\n                        continue;\n                    }\n\n                    $toggle = [];\n                }\n                // Recursively fetch the items.\n                $childData = $data[$key] ?? null;\n                if (null !== $childData && !is_array($childData)) {\n                    throw new \\RuntimeException(sprintf(\"Bad form data for field collection '%s': %s used instead of an array\", $key, gettype($childData)));\n                }\n                $data[$key] = $this->processFormRecursive($data[$key] ?? null, $toggle, $value);\n            } else {\n                $field = $this->get($value);\n                // Do not add the field if:\n                if (\n                    // Not an input field\n                    !$field\n                    // Field has been disabled\n                    || !empty($field['disabled'])\n                    // Field validation is set to be ignored\n                    || !empty($field['validate']['ignore'])\n                    // Field is overridable and the toggle is turned off\n                    || (!empty($field['overridable']) && empty($toggles[$key]))\n                ) {\n                    continue;\n                }\n                if (!isset($data[$key])) {","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Common/Data/BlueprintSchema.php#L352-L388","documentation":"BlueprintSchema::processFormRecursive() walks nested blueprint definitions. Whenever a blueprint node is itself an array, the corresponding submitted value may be null or absent, but if it exists it must be an array. A string, integer, boolean, or other scalar at that node throws RuntimeException naming the collection key and the actual PHP type.","triggerScenarios":"Call Blueprint::processForm() or BlueprintSchema::processForm() with ['address' => 'Plain text'] while the address field contains nested child fields. The check at BlueprintSchema.php:368-371 sees a non-array child value and aborts processing.","commonSituations":"A browser submits a flattened field name, an API client sends a string where the schema defines an object, a form-building plugin collapses nested fields, or old data is passed to a blueprint that was later changed to a nested collection.","solutions":["Fix the client or form field names so every nested collection submits an array, such as address[street] rather than address.","Validate incoming request data against the blueprint shape and return HTTP 400 before processForm().","If the field is genuinely scalar, change the blueprint to a flat field instead of a nested collection.","Sanitize legacy data before processing it, but do not silently cast arbitrary text to an array when validation matters."],"exampleFix":"// before\n$data = ['address' => '123 Main Street'];\n$processed = $blueprint->processForm($data); // Bad form data ... string used instead of an array\n\n// after\n$data = ['address' => ['street' => '123 Main Street']];\n$processed = $blueprint->processForm($data);","handlingStrategy":"validation","validationCode":"foreach (['address', 'contact'] as $collection) {\n    if (array_key_exists($collection, $data) && !is_array($data[$collection]) && $data[$collection] !== null) {\n        throw new InvalidArgumentException(sprintf('Field collection \"%s\" must be an array.', $collection));\n    }\n}\n$processed = $blueprint->processForm($data);","typeGuard":"function isNullableFormFieldArray(mixed $value): bool\n{\n    return $value === null || is_array($value);\n}","tryCatchPattern":"try {\n    $processed = $blueprint->processForm($data);\n} catch (RuntimeException $e) {\n    return $response->withStatus(400)->withJson(['error' => $e->getMessage()]);\n}","preventionTips":["Generate form-field names from the blueprint so nested collections post arrays.","Validate API request shapes before calling processForm().","Return 400 for malformed submissions rather than coercing them.","Add tests for scalar values at every nested collection node.","Version blueprint structure changes and migrate legacy data explicitly."],"tags":["grav","blueprint","form-validation","data-type","php"],"backgroundTag":"form-schema-type-mismatch","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}