symfony/symfony · error · RuntimeException
The container parameter "%s" resolves to an env var, which i
Error message
The container parameter "%s" resolves to an env var, which is not allowed in routing configuration.
What it means
Thrown by Router::resolve() after a parameter has already been resolved. Even if the placeholder name is a normal parameter, Symfony checks the resolved string value against the internal env-placeholder fingerprint (env_<16 hex>_<name>_<32 hex>, the format the DotEnv/EnvVarProcessor produces for processed env placeholders). If the parameter ultimately resolves to such an env placeholder, embedding it in routing is still forbidden, because the value is not stable at compile time.
Source
Thrown at src/Symfony/Bundle/FrameworkBundle/Routing/Router.php:181
if (!\is_string($value)) {
return $value;
}
$escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($value) {
// skip %%
if (!isset($match[1])) {
return '%%';
}
if (preg_match('/^env\((?:\w++:)*+\w++\)$/', $match[1])) {
throw new RuntimeException(\sprintf('Using "%%%s%%" is not allowed in routing configuration.', $match[1]));
}
$resolved = ($this->paramFetcher)($match[1]);
if (\is_string($resolved) && preg_match('/env_[a-f0-9]{16}_\w+_[a-f0-9]{32}/Ui', $resolved)) {
throw new RuntimeException(\sprintf('The container parameter "%s" resolves to an env var, which is not allowed in routing configuration.', $match[1]));
}
if (\is_scalar($resolved)) {
$this->collectedParameters[$match[1]] = $resolved;
if (\is_string($resolved)) {
$resolved = $this->resolve($resolved);
}
if (\is_scalar($resolved)) {
return false === $resolved ? '0' : (string) $resolved;
}
}
throw new RuntimeException(\sprintf('The container parameter "%s", used in the route configuration value "%s", must be a string or numeric, but it is of type "%s".', $match[1], $value, get_debug_type($resolved)));
}, $value);
return str_replace('%%', '%', $escapedValue);View on GitHub (pinned to 698e28026c)
Solutions
- Make the parameter fully concrete at compile time (e.g. set it in services.yaml with a literal default, or resolve env into a non-env placeholder via a compiler pass / extension that reads getEnv() and sets a real string).
- Use a kernel.event_listener or a custom URL generator decorator to mutate the generated URL/host at request time instead of baking an env placeholder into routing.
- Run 'php bin/console debug:container --env-vars' and 'php bin/console debug:container --parameters' to confirm which parameters still resolve to env placeholders, then eliminate them from route references.
Example fix
// before
# config/services.yaml
parameters:
app.base_host: '%env(resolve:APP_BASE_HOST)%'
# route
host: '%app.base_host%'
// after - resolve the env into a real string in an extension at compile time
# src/Kernel.php override registerForConfiguration / build()
$container->setParameter('app.base_host', $_ENV['APP_BASE_HOST'] ?? 'example.com');
# route stays the same
host: '%app.base_host%' Defensive patterns
Strategy: validation
Validate before calling
// Confirm no parameter referenced by routing resolves to an internal env placeholder.
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
function routingParamsResolveToRealValues(ParameterBag $bag, array $routeParamNames): array
{
$bad = [];
foreach ($routeParamNames as $name) {
if (!$bag->has($name)) { continue; }
$val = $bag->get($name);
if (is_string($val) && preg_match('/env_[a-f0-9]{16}_\w+_[a-f0-9]{32}/Ui', $val)) {
$bad[] = $name;
}
}
return $bad;
} Prevention
- Avoid chaining %env(...)% through parameters that end up in routes; resolve env vars to concrete strings in a compiler pass.
- Use 'php bin/console debug:container --env-vars' to see which parameters are env-backed before referencing them in routing.
- Keep route-referenced parameters literal in config (not env-derived) so cache warmup is deterministic.
When it happens
Trigger: You define a parameter like app.host: '%env(resolve:APP_HOST)%' (a valid plain parameter name), then reference %app.host% in a route host/path. Router fetches the parameter, gets the internal env_<...> placeholder string, matches the regex at line 180, and throws at line 181.
Common situations: Happens after developers think they avoided the direct %env(...)% form by wrapping it in a parameter, but the parameter still resolves transitively to an env placeholder. Common when binding env-driven domain names, scheme, or locale prefixes into routes.
Related errors
- Using "%%%s%%" is not allowed in routing configuration.
- The container parameter "%s", used in the route configuratio
- The route "%s" does not exist.
- Invalid Messenger routing configuration: invalid namespace "
- Invalid Messenger routing configuration: class or interface
AI-assisted analysis of symfony/symfony@698e28026c (2026-08-06).
Data as JSON: /api/errors/32dea87cbf29e463.
Report an issue: GitHub.