getgrav/grav · error · RuntimeException
Failed to update {file}: {message}
Error message
Failed to update {file}: {message} What it means
YamlUpdater::save() re-serializes a YAML file it previously parsed into lines/items, tries a line-preserving update, falls back to Yaml::dump, and writes the result. Any exception thrown inside — Symfony Yaml parse/dump errors on malformed content or edge-case indentation, or file write problems — is caught and rethrown as RuntimeException('Failed to update <file>: <inner message>'), where {file} is basename(filename) and {message} is the original exception text. The wrapper identifies which config file broke and preserves the root cause.
Source
Thrown at system/src/Grav/Installer/YamlUpdater.php:77
try {
if (!$this->isHandWritten()) {
$yaml = Yaml::dump($this->items, 5, 2);
} else {
$yaml = implode("\n", $this->lines);
$items = Yaml::parse($yaml);
if ($items !== $this->items) {
// Lines and items are out of sync — fall back to dumping
// from items directly. Loses original formatting but
// guarantees the file content matches the intended state.
$yaml = Yaml::dump($this->items, 5, 2);
}
}
file_put_contents($this->filename, $yaml);
} catch (\Exception $e) {
throw new \RuntimeException('Failed to update ' . basename($this->filename) . ': ' . $e->getMessage());
}
return true;
}
/**
* @return bool
*/
public function isHandWritten(): bool
{
return !empty($this->comments);
}
/**
* @return array
*/
public function getComments(): array
{View on GitHub (pinned to 6040efed04)
Solutions
- Read the {message} part first — it is the underlying cause: a Yaml parse/dump error points at the offending syntax, a write warning points at permissions.
- For syntax issues, validate the file: php -r "echo json_encode(Symfony\Component\Yaml\Yaml::parseFile('user/config/xxx.yaml'));" or re-save it from the admin panel to normalize formatting, then retry the update.
- For write issues, make the file and its directory writable by the PHP process (chown/chmod, e.g. chmod 664 file && chmod 775 dir).
- Keep a backup of the YAML file before upgrades so you can restore and re-apply changes manually if the updater cannot round-trip hand-written formatting.
Example fix
# before: root-owned config file, updater save fails $ ls -l user/config/system.yaml -rw-r--r-- 1 root root 4021 ... # web user cannot write # after $ chown www-data:www-data user/config/system.yaml && chmod 664 user/config/system.yaml $ bin/grav clearcache
Defensive patterns
Strategy: try-catch
Validate before calling
// Preflight: file parseable and writable before running updates
$updater = \Grav\Installer\YamlUpdater::load($filename) ?? \Grav\Installer\YamlUpdater::loadOrCreate($filename);
if (!\is_writable($filename) || !\is_writable(\dirname($filename))) {
throw new \RuntimeException("{$filename} is not writable; fix permissions first.");
}
try {
\Symfony\Component\Yaml\Yaml::parseFile($filename); // catches malformed YAML early
} catch (\Symfony\Component\Yaml\Exception\ParseException $e) {
throw new \RuntimeException('Fix YAML syntax first: ' . $e->getMessage());
} Try / catch
try {
$updater->save();
} catch (\RuntimeException $e) {
if (str_starts_with($e->getMessage(), 'Failed to update ')) {
// message embeds basename + root cause; surface it, keep a backup, and retry after fix
$log->error($e->getMessage());
$updater->backup(); // copy the original file aside before any manual edit
} else {
throw $e;
}
} Prevention
- Keep config/*.yaml owned and writable by the PHP process; check with is_writable() in deploy scripts.
- Validate YAML syntax after hand edits (Symfony Yaml::parseFile or a linter) before running upgrades.
- Back up user/config before plugin/theme/Grav upgrades so a failed round-trip update can be restored.
When it happens
Trigger: Running $updater->save() after executing updates against a YAML file that contains syntax the updater's line/item diff cannot round-trip and Symfony Yaml also fails to parse/dump (mixed tabs, control characters, broken multiline strings); target file or its directory not writable so file_put_contents throws; concurrent modification leaving the parsed state inconsistent.
Common situations: Grav/plugin upgrades rewriting config/system.yaml, user/config files, or .yaml blueprints that were hand-edited with unusual formatting; deployments where config files are root-owned; YAML files containing PHP-style constants or custom tags Symfony Yaml rejects.
Related errors
- Could not update system configuration to maintain backwards
- Could not update system configuration for Twig compatibility
- Could not remove the retired security.*.xss_scan_output sett
- Could not migrate the Twig-sandbox allowlists to the additiv
- Versions file cannot be read
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/60b7f931572a95d6.
Report an issue: GitHub.