{"record":{"id":"fe029ead953c8f6f","repo":"getgrav/grav","slug":"json-encoding-failed-s-encoding-s","errorCode":null,"errorMessage":"JSON encoding failed: %s. Encoding: %s","messagePattern":"JSON encoding failed: (.+?)\\. Encoding: (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/Compat/Monolog/Utils.php","lineNumber":163,"sourceCode":"        {\n            switch ($code) {\n                case JSON_ERROR_DEPTH:\n                    $msg = 'Maximum stack depth exceeded';\n                    break;\n                case JSON_ERROR_STATE_MISMATCH:\n                    $msg = 'Underflow or the modes mismatch';\n                    break;\n                case JSON_ERROR_CTRL_CHAR:\n                    $msg = 'Unexpected control character found';\n                    break;\n                case JSON_ERROR_UTF8:\n                    $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';\n                    break;\n                default:\n                    $msg = 'Unknown error';\n            }\n\n            throw new \\RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true));\n        }\n\n        /**\n         * @param mixed $data\n         */\n        public static function detectAndCleanUtf8(&$data)\n        {\n            if (is_string($data) && !preg_match('//u', $data)) {\n                $data = preg_replace_callback(\n                    '/[\\x80-\\xFF]+/',\n                    static function ($m) { return utf8_encode($m[0]); },\n                    $data\n                );\n                $data = str_replace(\n                    ['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'],\n                    ['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'],\n                    $data\n                );","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/Compat/Monolog/Utils.php#L145-L181","documentation":"Grav ships a compat copy of Monolog's Utils (system/src/Grav/Framework/Compat/Monolog/Utils.php:163) whose JSON encoder throws a RuntimeException embedding the json_last_error message (e.g. 'Malformed UTF-8 characters') and a var_export of the offending data whenever json_encode() fails. It fires while a log record is normalized/encoded, so the culprit is data you tried to log, not the logger config.","triggerScenarios":"Logging a record containing invalid UTF-8 — raw binary from a database or uploaded file, strings cropped mid-multibyte-sequence by substr(), or legacy encodings (ISO-8859-1/Windows-1252) never converted; debug handlers serializing objects with binary/resource properties.","commonSituations":"Production sites logging third-party API responses in legacy encodings; logging raw request bodies that include binary uploads; data corrupted by naive string truncation before logging.","solutions":["Sanitize before logging: mb_convert_encoding($value, 'UTF-8', 'UTF-8') strips/replaces invalid sequences; Grav's compat Utils::detectAndCleanUtf8() exists for the same purpose.","Do not log raw binary or unknown-encoding payloads — log identifiers, lengths, or hex dumps instead.","Keep Grav/Monolog current so encoding failures are handled without aborting the log call."],"exampleFix":"// before\n$grav['log']->error('Payload: ' . $rawBody); // invalid UTF-8 -> RuntimeException\n\n// after\n$clean = mb_convert_encoding($rawBody, 'UTF-8', 'UTF-8');\n$grav['log']->error('Payload: ' . $clean);","handlingStrategy":"fallback","validationCode":"$clean = array_map(\n    static fn($v) => \\is_string($v) ? mb_convert_encoding($v, 'UTF-8', 'UTF-8') : $v,\n    $context\n);\n$grav['log']->error('Context', $clean);","typeGuard":"function isUtf8(string $value): bool\n{\n    return (bool) preg_match('//u', $value);\n}","tryCatchPattern":"try {\n    $grav['log']->error($message, $context);\n} catch (\\RuntimeException $e) {\n    if (str_starts_with($e->getMessage(), 'JSON encoding failed')) {\n        $grav['log']->error(mb_convert_encoding($message, 'UTF-8', 'UTF-8'));\n    }\n}","preventionTips":["Sanitize external data with mb_convert_encoding($s, 'UTF-8', 'UTF-8') before logging.","Never log raw binary bodies; log IDs, sizes, or bin2hex() snippets.","Avoid substr() on multibyte strings before logging — use mb_substr()."],"tags":["logging","json","utf-8","monolog","encoding"],"backgroundTag":"json-encoding-invalid-utf8","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}