{"record":{"id":"ca2a1ddfd68f48c7","repo":"getgrav/grav","slug":"bad-data","errorCode":null,"errorMessage":"Bad data","messagePattern":"Bad data","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/ContentBlock/ContentBlock.php","lineNumber":70,"sourceCode":"     */\n    public static function create($id = null)\n    {\n        return new static($id);\n    }\n\n    /**\n     * @param array $serialized\n     * @return ContentBlockInterface\n     * @throws InvalidArgumentException\n     */\n    public static function fromArray(array $serialized)\n    {\n        try {\n            $type = $serialized['_type'] ?? null;\n            $id = $serialized['id'] ?? null;\n\n            if (!$type || !$id || !is_a($type, ContentBlockInterface::class, true)) {\n                throw new InvalidArgumentException('Bad data');\n            }\n\n            /** @var ContentBlockInterface $instance */\n            $instance = new $type($id);\n            $instance->build($serialized);\n        } catch (Exception $e) {\n            throw new InvalidArgumentException(sprintf('Cannot unserialize Block: %s', $e->getMessage()), $e->getCode(), $e);\n        }\n\n        return $instance;\n    }\n\n    /**\n     * Block constructor.\n     *\n     * @param string|null $id\n     */\n    public function __construct($id = null)","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/ContentBlock/ContentBlock.php#L52-L88","documentation":"Thrown by ContentBlock::fromArray() when the serialized array cannot be identified as a content block: the '_type' key is missing or falsy, the 'id' key is missing or falsy, or '_type' does not name an existing class implementing ContentBlockInterface. fromArray() is the counterpart of toArray() (which emits '_type', '_version' and 'id'), so any other shape is rejected. The surrounding catch immediately re-wraps it as 'Cannot unserialize Block: Bad data' with the original exception chained.","triggerScenarios":"Calling ContentBlock::fromArray($array) with an array that was not produced by ContentBlock::toArray(): '_type' or 'id' key missing; '_type' naming a deleted, renamed or moved class (e.g. after refactoring a custom block class); '_type' naming a class that exists but no longer implements ContentBlockInterface; nested 'blocks' entries with the same problems.","commonSituations":"Replaying a cache written by an older Grav release after block classes moved; plugins hand-building block arrays instead of round-tripping toArray(); corrupted cache backends returning truncated-but-valid JSON; passing user-supplied arrays straight into fromArray().","solutions":["Regenerate the source array from a live block: it must contain '_type' (class name), '_version' and 'id' — confirm with a fresh $block->toArray() before calling fromArray().","If the array comes from Grav's cache, clear it (bin/grav clear-cache) and let the blocks rebuild with current class names.","After upgrades/refactors, verify the class still resolves: class_exists($type) && is_a($type, ContentBlockInterface::class, true).","Catch InvalidArgumentException around fromArray() and rebuild the block from its source content as a fallback."],"exampleFix":"// before\n$block = ContentBlock::fromArray($cached['block'] ?? []);\n\n// after\n$data = $cached['block'] ?? [];\n$type = $data['_type'] ?? null;\nif (!$type || !($data['id'] ?? null) || !is_a($type, ContentBlockInterface::class, true)) {\n    $block = ContentBlock::fromArray($freshBlock->toArray()); // rebuild from source\n} else {\n    $block = ContentBlock::fromArray($data);\n}","handlingStrategy":"validation","validationCode":"$type = $serialized['_type'] ?? null;\n$id = $serialized['id'] ?? null;\nif (!$type || !$id || !is_a($type, \\Grav\\Framework\\ContentBlock\\ContentBlockInterface::class, true)) {\n    // do not call fromArray(); rebuild the block from source content\n}","typeGuard":"function isSerializedContentBlock(array $data): bool\n{\n    $type = $data['_type'] ?? null;\n    return (bool) $type && (bool) ($data['id'] ?? null)\n        && is_a($type, \\Grav\\Framework\\ContentBlock\\ContentBlockInterface::class, true);\n}","tryCatchPattern":"try {\n    $block = ContentBlock::fromArray($data);\n} catch (\\InvalidArgumentException $e) {\n    $block = $source->buildBlock(); // regenerate; $e->getPrevious() holds the root cause\n}","preventionTips":["Only pass arrays produced by toArray() into fromArray(); never hand-craft the shape.","Version-stamp cache keys that persist block arrays and invalidate them on Grav upgrades.","Add round-trip tests (toArray -> fromArray) for custom block classes in CI."],"tags":["php","grav","content-block","unserialization","invalid-data"],"backgroundTag":"unserialize-invalid-data","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}