getgrav/grav · error · InvalidArgumentException

Cannot unserialize Block: %s

Error message

Cannot unserialize Block: %s

What it means

Umbrella exception from ContentBlock::fromArray(): any Exception raised while reconstructing the block — the 'Bad data' guard, a failure inside `new $type($id)`, or anything thrown by $instance->build($serialized) (build() runs checkVersion() and recurses into self::fromArray() for every nested block) — is caught and re-thrown as this InvalidArgumentException with the original message interpolated via sprintf and the original exception chained as previous. Read $e->getPrevious() for the root cause.

Source

Thrown at system/src/Grav/Framework/ContentBlock/ContentBlock.php:77

     * @param array $serialized
     * @return ContentBlockInterface
     * @throws InvalidArgumentException
     */
    public static function fromArray(array $serialized)
    {
        try {
            $type = $serialized['_type'] ?? null;
            $id = $serialized['id'] ?? null;

            if (!$type || !$id || !is_a($type, ContentBlockInterface::class, true)) {
                throw new InvalidArgumentException('Bad data');
            }

            /** @var ContentBlockInterface $instance */
            $instance = new $type($id);
            $instance->build($serialized);
        } catch (Exception $e) {
            throw new InvalidArgumentException(sprintf('Cannot unserialize Block: %s', $e->getMessage()), $e->getCode(), $e);
        }

        return $instance;
    }

    /**
     * Block constructor.
     *
     * @param string|null $id
     */
    public function __construct($id = null)
    {
        $this->id = $id ? (string) $id : $this->generateId();
    }

    /**
     * @return string
     */

View on GitHub (pinned to 6040efed04)

Solutions

  1. Inspect $e->getPrevious() — it carries the real cause ('Bad data', 'Unsupported version N', or a constructor/build error).
  2. Clear the cache (bin/grav clear-cache) so blocks regenerate under the current version and class names.
  3. Fix the referenced block class so that `new $type($id)` and build() cannot throw on well-formed data.
  4. Catch this exception at the call site and regenerate the block from source content instead of surfacing the failure.

Example fix

// before
$block = ContentBlock::fromArray($cached);

// after
try {
    $block = ContentBlock::fromArray($cached);
} catch (\InvalidArgumentException $e) {
    $log->warning('Block unserialize failed: ' . $e->getMessage(), ['previous' => $e->getPrevious()]);
    $block = $page->buildContentBlock(); // regenerate from source
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    $block = ContentBlock::fromArray($cached);
} catch (\InvalidArgumentException $e) {
    // umbrella failure: inspect $e->getPrevious(), then regenerate
    $log->warning($e->getMessage(), ['root' => $e->getPrevious()]);
    $block = $page->buildContentBlock();
}

Prevention

When it happens

Trigger: fromArray() on partially corrupted arrays; a '_version' mismatch bubbling up from build() as 'Unsupported version N'; a custom block class whose constructor or build() throws; deeply nested block trees where an inner block fails validation.

Common situations: Upgrading Grav with stale cached pages; cache/session backends returning truncated data; custom block implementations that throw during build(); data serialized by one Grav version replayed by another (dev cache imported into prod, rollback after upgrade).

Related errors


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/de58e64fb9b4295d. Report an issue: GitHub.