{"record":{"id":"c6a73cc95fd679c2","repo":"getgrav/grav","slug":"json-encode-failed-to-encode-group-permissions","errorCode":null,"errorMessage":"json_encode(): failed to encode group permissions","messagePattern":"json_encode\\(\\): failed to encode group permissions","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/Flex/Pages/FlexPageObject.php","lineNumber":206,"sourceCode":"     */\n    public function getFormValue(string $name, $default = null, ?string $separator = null)\n    {\n        $test = new stdClass();\n\n        $value = $this->pageContentValue($name, $test);\n        if ($value !== $test) {\n            return $value;\n        }\n\n        switch ($name) {\n            case 'name':\n                return $this->getProperty('template');\n            case 'route':\n                return $this->hasKey() ? '/' . $this->getKey() : null;\n            case 'header.permissions.groups':\n                $encoded = json_encode($this->getPermissions());\n                if ($encoded === false) {\n                    throw new RuntimeException('json_encode(): failed to encode group permissions');\n                }\n\n                return json_decode($encoded, true);\n        }\n\n        return parent::getFormValue($name, $default, $separator);\n    }\n\n    /**\n     * Get master storage key.\n     *\n     * @return string\n     * @see FlexObjectInterface::getStorageKey()\n     */\n    public function getMasterKey(): string\n    {\n        $key = (string)($this->storage_key ?? $this->getMetaData()['storage_key'] ?? null);\n        if (($pos = strpos($key, '|')) !== false) {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/Flex/Pages/FlexPageObject.php#L188-L224","documentation":"When a form asks FlexPageObject for 'header.permissions.groups', the value is round-tripped through json_encode() to normalize it; if encoding fails (false), Grav throws this RuntimeException. json_encode() fails on invalid UTF-8 sequences, resources, INF/NAN floats, or deeply recursive arrays. The permissions data itself is malformed for JSON, usually because the page frontmatter or a custom permission resolver produced non-encodable values.","triggerScenarios":"Page header permissions.groups containing invalid UTF-8 bytes (pasted binary/smart characters from a bad editor); a plugin injecting resources or objects into group permission arrays; group values containing NAN/INF from arithmetic; array recursion from self-referencing permission structures.","commonSituations":"Frontmatter edited in a non-UTF-8 editor or copied from word processors; third-party access-control plugins that attach runtime objects to permission groups.","solutions":["Fix the source data: ensure header.permissions.groups in the page file uses valid UTF-8 strings and plain arrays.","json_last_error() after a failed encode to identify the cause (JSON_ERROR_UTF8 vs JSON_ERROR_RECURSION etc.) and strip/sanitize accordingly (mb_convert_encoding, removing resources).","If a plugin modifies permissions at runtime, make it return scalar arrays of permission strings only.","Catch the RuntimeException around form rendering to degrade gracefully with an admin notice instead of a 500."],"exampleFix":"// before (frontmatter)\npermissions:\n  groups:\n    site-editors: [\"\\xB1-invalid-utf8\"]\n\n# after\npermissions:\n  groups:\n    site-editors: [\"pages.read\", \"pages.update\"]","handlingStrategy":"try-catch","validationCode":"// Validate group permissions are JSON-safe before rendering forms\n$groups = $page->getPermissions();\nif (json_encode($groups) === false) {\n    // identify cause: json_last_error_msg(); sanitize invalid UTF-8, drop resources\n    $groups = array_map(\n        fn($v) => is_string($v) ? mb_convert_encoding($v, 'UTF-8', 'UTF-8') : $v,\n        $groups\n    );\n}","typeGuard":null,"tryCatchPattern":"try {\n    $value = $page->getFormValue('header.permissions.groups');\n} catch (\\Grav\\Framework\\Flex\\Exception\\RuntimeException $e) {\n    // permissions data not JSON-encodable: flag the page for repair instead of a hard 500\n    $admin->setMessage('Page ' . $page->route() . ' has malformed permissions data', 'error');\n}","preventionTips":["Keep permissions.groups values as plain arrays of permission strings in valid UTF-8.","Edit frontmatter in UTF-8 editors only.","If plugins alter permissions at runtime, have them return scalars/arrays, never resources or objects."],"tags":["grav","flex","json","permissions","utf-8"],"backgroundTag":"json-encode-failed","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}