getgrav/grav · critical · InstallException
Could not migrate the Twig-sandbox allowlists to the additiv
Error message
Could not migrate the Twig-sandbox allowlists to the additive-defaults model
What it means
This InstallException is thrown by the Grav 1.8 postflight update that migrates security.twig_sandbox.allowed_* lists from the old replace semantics to additive defaults. The routine reads user/config/security.yaml, asks Security::planSandboxDefaultsMigration() for omitted defaults that must become denied_* entries, and saves the file with YamlUpdater. Any exception during read, planning, update, or save is wrapped as this installation failure so an upgrade cannot silently lose a tightened Twig-sandbox policy.
Source
Thrown at system/src/Grav/Installer/updates/1.8.0_2026-08-12_0.php:74
}
}
$yaml->define("twig_sandbox.{$key}", array_values($merged));
}
$yaml->save();
$summary = [];
foreach ($plan as $key => $additions) {
$summary[] = $key . ' (' . count($additions) . ')';
}
error_log(
'Grav upgrade: preserved tightened Twig-sandbox policy in '
. 'user/config/security.yaml by adding ' . implode(', ', $summary)
. '. Review these denials if any were defaults you actually want; '
. 'see security.twig_sandbox.denied_* in the file.'
);
} catch (\Exception $e) {
throw new InstallException('Could not migrate the Twig-sandbox allowlists to the additive-defaults model', $e);
}
}
];
View on GitHub (pinned to 6040efed04)
Solutions
- Inspect InstallException::getPrevious(); the wrapped exception names the real YAML, permission, or write failure.
- Make user/config/security.yaml and user/config readable and writable by the PHP process, for example chown www-data:www-data user/config/security.yaml and chmod 0644 user/config/security.yaml.
- Validate the YAML file, fix syntax errors or restore a known-good backup, then rerun the failed Grav update; the migration is documented as idempotent.
- If the update cannot be made writable, back up security.yaml, temporarily remove the twig_sandbox overrides, complete the update, then manually reproduce the old policy by adding every omitted default to the matching denied_* list.
- Retry the update after freeing disk space or remounting the configuration volume as writable.
Example fix
// before
chown root:root user/config/security.yaml
chmod 0400 user/config/security.yaml
# Grav update fails: Could not migrate the Twig-sandbox allowlists
// after
chown www-data:www-data user/config/security.yaml
chmod 0644 user/config/security.yaml
php -r 'exit(is_readable("user/config/security.yaml") ? 0 : 1);'
# rerun the Grav update; re-running merges the same denials safely Defensive patterns
Strategy: validation
Validate before calling
$file = GRAV_ROOT . '/user/config/security.yaml';
if (is_file($file)) {
if (!is_readable($file) || !is_writable($file)) {
throw new RuntimeException('Make user/config/security.yaml readable and writable before updating Grav.');
}
try {
Symfony\Component\Yaml\Yaml::parseFile($file);
} catch (Throwable $e) {
throw new RuntimeException('Fix security.yaml syntax before updating Grav: ' . $e->getMessage(), 0, $e);
}
} Try / catch
try {
$updater->update();
} catch (InstallException $e) {
$cause = $e->getPrevious();
log_error('Grav update failed: ' . $e->getMessage() . '; cause: ' . ($cause ? $cause->getMessage() : 'unknown'));
restore_configuration_backup();
throw $e;
} Prevention
- Keep user/config writable by the PHP process during Grav updates.
- Lint security.yaml in CI or a pre-deployment check.
- Back up security.yaml before every Grav upgrade.
- Test major upgrades on a staging copy when custom twig_sandbox allowlists exist.
- Never mark an update complete after this exception; inspect the wrapped cause first.
When it happens
Trigger: Running a Grav update that includes 1.8.0_2026-08-12_0.php while user/config/security.yaml exists and contains one or more tightened twig_sandbox allowed_* lists, and the file cannot be parsed or saved. Typical failing calls are YamlUpdater::instance($file), $yaml->define(...), or $yaml->save() when permissions are wrong, YAML is invalid, disk is full, or the config tree is read-only.
Common situations: Config files owned by root or an FTP user after deployment, read-only containers, invalid YAML such as tabs or duplicate keys, a full disk, or a restored backup with different ownership. Sites that intentionally removed default Twig tags, filters, functions, or classes from allowed_* are exactly the sites this migration tries to protect.
Related errors
- json_encode(): failed to encode dependencies
- Following requirements have failed:
- 400
- Could not update system configuration to maintain backwards
- Failed to update {file}: {message}
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/ef16aea4ce75390d.
Report an issue: GitHub.