{"record":{"id":"3daaf7a54004a2e0","repo":"getgrav/grav","slug":"bad-data-formatter","errorCode":null,"errorMessage":"Bad Data Formatter","messagePattern":"Bad Data Formatter","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/Flex/Storage/AbstractFilesystemStorage.php","lineNumber":131,"sourceCode":"            'key' => $key\n        ];\n    }\n\n    /**\n     * @param string|array $formatter\n     * @return void\n     */\n    protected function initDataFormatter($formatter): void\n    {\n        // Initialize formatter.\n        if (!is_array($formatter)) {\n            $formatter = ['class' => $formatter];\n        }\n        $formatterClassName = $formatter['class'] ?? JsonFormatter::class;\n        $formatterOptions = $formatter['options'] ?? [];\n\n        if (!is_a($formatterClassName, FileFormatterInterface::class, true)) {\n            throw new \\InvalidArgumentException('Bad Data Formatter');\n        }\n\n        $this->dataFormatter = new $formatterClassName($formatterOptions);\n    }\n\n    /**\n     * @param string $filename\n     * @return string|null\n     */\n    protected function detectDataFormatter(string $filename): ?string\n    {\n        if (preg_match('|(\\.[a-z0-9]*)$|ui', $filename, $matches)) {\n            switch ($matches[1]) {\n                case '.json':\n                    return JsonFormatter::class;\n                case '.yaml':\n                    return YamlFormatter::class;\n                case '.md':","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/Flex/Storage/AbstractFilesystemStorage.php#L113-L149","documentation":"AbstractFilesystemStorage::initDataFormatter() validates the configured storage formatter before instantiating it. The 'class' entry of the formatter config (or the bare class-name string) must be a class that implements FileFormatterInterface, otherwise an InvalidArgumentException('Bad Data Formatter') is thrown. This is a hard configuration error raised the moment a Flex directory with filesystem storage is created.","triggerScenarios":"Defining a Flex directory (blueprint or plugin config) whose storage options set 'formatter' to a class name that is not a FileFormatterInterface implementation; misspelling a built-in formatter class (e.g. 'Grav\\Framework\\File\\Formatter\\JsonFormater'); pointing 'class' at a class that only exists in a newer/older Grav version than the one installed; passing a namespaced string of a class that failed to autoload.","commonSituations":"Custom Flex directory with storage.formatter.class set to a project-local formatter that was deleted or moved; typo in the FQCN; upgrading Grav changed formatter namespaces (they live in Grav\\Framework\\File\\Formatter); copying a blueprint from another project whose custom formatter package is not installed.","solutions":["Open the Flex blueprint/storage config and verify storage.formatter.class is a fully-qualified class name implementing Grav\\Framework\\File\\Formatter\\FileFormatterInterface (check with is_a($class, FileFormatterInterface::class, true)).","Fix typos and use the built-in FQCNs: JsonFormatter, YamlFormatter, MarkdownFormatter, SerializeFormatter, IniFormatter in Grav\\Framework\\File\\Formatter.","If using a custom formatter, make sure the file is autoloaded (composer PSR-4 mapping, plugin class loaded) and the class implements FileFormatterInterface.","After fixing, clear cache so the directory is re-instantiated (bin/grav clearcache)."],"exampleFix":"# before (blueprint storage config)\nstorage:\n  class: Grav\\Framework\\Flex\\Storage\\FolderStorage\n  options:\n    formatter:\n      class: 'Grav\\Framework\\File\\Formatter\\JsonFormater'  # typo -> Bad Data Formatter\n\n# after\nstorage:\n  class: Grav\\Framework\\Flex\\Storage\\FolderStorage\n  options:\n    formatter:\n      class: 'Grav\\Framework\\File\\Formatter\\JsonFormatter'","handlingStrategy":"validation","validationCode":"use Grav\\Framework\\File\\Formatter\\FileFormatterInterface;\n\n$class = $storageConfig['options']['formatter']['class']\n    ?? $storageConfig['options']['formatter']\n    ?? \\Grav\\Framework\\File\\Formatter\\JsonFormatter::class;\n\nif (!\\is_string($class) || !\\is_a($class, FileFormatterInterface::class, true)) {\n    throw new \\InvalidArgumentException(\"Invalid formatter class: {$class}\");\n}","typeGuard":"function isValidFormatterClass(string $class): bool\n{\n    return \\class_exists($class)\n        && \\is_a($class, \\Grav\\Framework\\File\\Formatter\\FileFormatterInterface::class, true);\n}","tryCatchPattern":"try {\n    $directory = $flex->getDirectory($type);\n} catch (\\InvalidArgumentException $e) {\n    if ('Bad Data Formatter' === $e->getMessage()) {\n        // fall back to default JSON storage for this directory\n        $blueprint['storage']['options']['formatter'] = \\Grav\\Framework\\File\\Formatter\\JsonFormatter::class;\n        $directory = $flex->getDirectory($type);\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Always use fully-qualified built-in formatter class names from Grav\\Framework\\File\\Formatter.","In CI or a preflight script, assert every Flex storage formatter passes is_a($class, FileFormatterInterface::class, true).","When copying Flex blueprints between projects, verify the custom formatter package is in composer.json."],"tags":["flex","storage","formatter","configuration","grav"],"backgroundTag":"class-does-not-implement-interface","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}