getgrav/grav · error · InvalidArgumentException
Cannot unserialize Flex type '{$type}': Directory not found
Error message
Cannot unserialize Flex type '{$type}': Directory not found What it means
FlexObject::doUnserialize() rebuilds an object from serialized data. When no FlexDirectory is passed in, it asks the Flex container (Grav::instance()['flex']) for the directory registered under the serialized 'type'. If no directory is registered for that type, the object cannot be rehydrated and an InvalidArgumentException is thrown. This almost always means the Flex type existed when the object was cached/serialized, but is no longer registered at unserialize time.
Source
Thrown at system/src/Grav/Framework/Flex/FlexObject.php:1022
}
/**
* @param array $serialized
* @param FlexDirectory|null $directory
* @return void
*/
protected function doUnserialize(array $serialized, ?FlexDirectory $directory = null): void
{
$type = $serialized['type'] ?? 'unknown';
if (!isset($serialized['key'], $serialized['type'], $serialized['elements'])) {
throw new \InvalidArgumentException("Cannot unserialize '{$type}': Bad data");
}
if (null === $directory) {
$directory = $this->getFlexContainer()->getDirectory($type);
if (!$directory) {
throw new \InvalidArgumentException("Cannot unserialize Flex type '{$type}': Directory not found");
}
}
$this->setFlexDirectory($directory);
$this->setMetaData($serialized['storage']);
$this->setKey($serialized['key']);
$this->setElements($serialized['elements']);
}
/**
* @return array
*/
protected function getTemplateConfig()
{
$config = $this->getFlexDirectory()->getConfig('site.templates', []);
$defaults = array_replace($config['defaults'] ?? [], $config['object']['defaults'] ?? []);
$config['object']['defaults'] = $defaults;
View on GitHub (pinned to 6040efed04)
Solutions
- Check that the Flex type is still registered: dump Grav::instance()['flex']->getDirectories() and confirm the type key from the serialized data exists.
- If a plugin provided the directory, re-enable it or update the blueprint/collection definition so the directory is registered under the same type name.
- Clear stale cache (cache/ folder, or bin/grav clearcache) so old serialized Flex objects referencing the removed type are purged.
- If the type was intentionally renamed, migrate the serialized data (update its 'type' entry) or pass an explicit FlexDirectory to doUnserialize via the object's unserialize path instead of relying on container lookup.
- In CLI/test contexts, boot Grav (e.g. Grav::instance()['accounts'] trigger or load the flex-objects plugin) before unserializing Flex objects.
Example fix
// before
$object = unserialize($cachedString); // Flex type 'old-type' no longer registered -> throws
// after
$flex = Grav::instance()['flex'];
$type = $serialized['type'] ?? null;
$directory = $type ? $flex->getDirectory($type) : null;
if (null === $directory) {
// regenerate instead of unserializing stale data
$object = $flex->getDirectory('new-type')->getObject($key);
} else {
$object = unserialize($cachedString);
} Defensive patterns
Strategy: validation
Validate before calling
$flex = \Grav\Common\Grav::instance()['flex'] ?? null;
$type = $serialized['type'] ?? null;
$directory = ($flex && $type) ? $flex->getDirectory($type) : null;
if (null === $directory) {
// skip unserialize; regenerate the object or log & drop stale cache
return $flex ? $flex->getDirectory('fallback-type')->getObject($key) : null;
}
$object = unserialize($cachedString); Type guard
/** @param array{type?: string} $serialized */
function flexTypeIsRegistered(array $serialized): bool
{
$flex = \Grav\Common\Grav::instance()['flex'] ?? null;
$type = $serialized['type'] ?? null;
return $flex instanceof \Grav\Framework\Flex\Interfaces\FlexContainerInterface
&& \is_string($type)
&& $flex->getDirectory($type) instanceof \Grav\Framework\Flex\FlexDirectory;
} Try / catch
try {
$object = unserialize($cached);
} catch (\InvalidArgumentException $e) {
if (str_contains($e->getMessage(), 'Directory not found')) {
// stale cache entry for a removed Flex type: invalidate and regenerate
$cache->delete($key);
$object = $flex->getDirectory($type)->getObject($objectKey);
} else {
throw $e;
}
} Prevention
- Register all Flex directories (plugins enabled) before any code that unserializes Flex objects, including CLI and cron contexts.
- When renaming or removing a Flex type, ship a migration that clears caches containing serialized objects of the old type.
- Version-prefix cache keys with the Flex type name so invalidation on type changes is automatic.
When it happens
Trigger: Calling unserialize() (or $flexObject->__unserialize()/unserialize(string) on a Flex object) whose serialized 'type' has no matching directory in the Flex container; fetching an object from cache after the blueprint or plugin providing that Flex type was removed, renamed, or disabled; unserializing a Flex object before the plugin that registers its directory has booted.
Common situations: A plugin (e.g. flex-objects with a custom directory) is deactivated or its blueprint type is renamed while cached data still references the old type; upgrading Grav or a plugin changes/deprecates a Flex type; running code in a context (CLI, tests) where the Flex container was never initialized with that directory.
Related errors
- Cache folder not defined.
- At least one cache must be specified
- The class '%s' does not implement the '%s' interface
- Cache keys must be array or Traversable, "%s" given
- Cache values must be array or Traversable, "%s" given
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/ea09e6f6ef6fe9fa.
Report an issue: GitHub.