getgrav/grav · error · RuntimeException
Encoding markdown failed
Error message
Encoding markdown failed
What it means
MarkdownFormatter::encode() assembles the YAML frontmatter and body, then normalizes line endings with preg_replace("/(\r\n|\r)/u", "\n", $encoded). The /u modifier makes PCRE validate the subject as UTF-8; invalid bytes force preg_replace to return null, which this null-check converts into a RuntimeException. Cause: the header array's encoded form or the body string is not valid UTF-8.
Source
Thrown at system/src/Grav/Framework/File/Formatter/MarkdownFormatter.php:103
public function encode($data): string
{
$headerVar = $this->getHeaderField();
$bodyVar = $this->getBodyField();
$header = isset($data[$headerVar]) ? (array) $data[$headerVar] : [];
$body = isset($data[$bodyVar]) ? (string) $data[$bodyVar] : '';
// Create Markdown file with YAML header.
$encoded = '';
if ($header) {
$encoded = "---\n" . trim($this->getHeaderFormatter()->encode($data['header'])) . "\n---\n\n";
}
$encoded .= $body;
// Normalize line endings to Unix style.
$encoded = preg_replace("/(\r\n|\r)/u", "\n", $encoded);
if (null === $encoded) {
throw new RuntimeException('Encoding markdown failed');
}
return $encoded;
}
/**
* {@inheritdoc}
* @see FileFormatterInterface::decode()
*/
public function decode($data): array
{
$headerVar = $this->getHeaderField();
$bodyVar = $this->getBodyField();
$rawVar = $this->getRawField();
// Define empty content
$content = [
$headerVar => [],View on GitHub (pinned to 6040efed04)
Solutions
- Convert the source encoding before saving: mb_convert_encoding($body, 'UTF-8', 'Windows-1252') (identify the real source encoding first).
- Drop invalid sequences lossily: mb_convert_encoding($s, 'UTF-8', 'UTF-8') or iconv('UTF-8', 'UTF-8//IGNORE', $s).
- Guard inputs with mb_check_encoding($string, 'UTF-8') and reject or sanitize earlier in the pipeline.
- Fix the storage layer (utf8mb4 connection charset) so strings arrive clean.
Example fix
// before
$file->save(['header' => $header, 'markdown' => $body]);
// after
$body = mb_convert_encoding((string) $body, 'UTF-8', 'UTF-8');
array_walk_recursive($header, function (&$v) {
if (is_string($v)) { $v = mb_convert_encoding($v, 'UTF-8', 'UTF-8'); }
});
$file->save(['header' => $header, 'markdown' => $body]); Defensive patterns
Strategy: validation
Validate before calling
// Validate both parts before encode()
$bodyOk = mb_check_encoding((string) ($data['markdown'] ?? ''), 'UTF-8');
$headerOk = true;
array_walk_recursive($data['header'] ?? [], function ($v) use (&$headerOk) {
if (is_string($v)) { $headerOk = $headerOk && mb_check_encoding($v, 'UTF-8'); }
});
if (!$bodyOk || !$headerOk) {
// convert/drop invalid bytes before calling the formatter
} Type guard
function isUtf8String(mixed $value): bool
{
return is_string($value) && mb_check_encoding($value, 'UTF-8');
} Try / catch
try {
$file->save($data);
} catch (\RuntimeException $e) {
if (str_contains($e->getMessage(), 'Encoding markdown failed')) {
$data['markdown'] = mb_convert_encoding($data['markdown'], 'UTF-8', 'UTF-8');
$file->save($data); // retry once after sanitizing
}
} Prevention
- Enforce UTF-8 at every input boundary (forms, imports, feeds).
- Keep DB connections on utf8mb4 so frontmatter values are clean.
- Reject or sanitize binary uploads before they reach page data.
When it happens
Trigger: Encoding a markdown file whose frontmatter values or body contain non-UTF-8 bytes: text pasted from Windows-1252/latin1 sources, strings from non-utf8mb4 DB columns, double-encoded or binary-contaminated content flowing into $data['header'] or the body field.
Common situations: Content pasted from word processors in legacy encodings; legacy site imports; DB connections without utf8mb4; metadata (titles, summaries) captured from external feeds with broken encoding.
Related errors
- Decoding markdown failed
- Encoding JSON failed: {json_last_error_msg}
- json_encode(): failed to encode dependencies
- Bad data
- Cannot unserialize Block: %s
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/ccdd219d1efc1a9c.
Report an issue: GitHub.