symfony/routing · warning · BadMethodCallException

Cannot unserialize Symfony\Component\Routing\Route

Error message

Cannot unserialize Symfony\Component\Routing\Route

What it means

Route::__unserialize() guards against unserializing attacker-crafted payloads: if path, host, or condition come back as Stringable objects, deserialization could trigger arbitrary code via __toString(), so Symfony refuses with this BadMethodCallException. It only fires on malicious or hand-mangled serialized data — legitimate serialized Route objects contain plain strings.

Solutions

  1. Never unserialize untrusted input; use json_encode/json_decode or a signed serialization format instead
  2. If you must unserialize, validate/allow-list the payload structure and classes first (e.g. allowed_classes: [])
  3. Regenerate the serialized data from a trusted source; do not attempt to hand-craft Route payloads

Example fix

// before
$route = unserialize($userInput);
// after
$data = json_decode($userInput, true);
$route = (new Route($data['path'] ?? '/'))->setHost($data['host'] ?? '');
Defensive patterns

Strategy: type-guard

Validate before calling

$data = unserialize($payload, ['allowed_classes' => false]); if (isset($data['path']) && is_object($data['path'])) { throw new \RuntimeException('Suspicious Route payload'); }

Type guard

function safeRoutePayload(array $data): bool { foreach (['path','host','condition'] as $k) { if (isset($data[$k]) && $data[$k] instanceof \Stringable) return false; } return true; }

Try / catch

try { $route = unserialize($payload); } catch (\BadMethodCallException $e) { if (str_contains($e->getMessage(), 'Cannot unserialize Symfony\\Component\\Routing\\Route')) { /* reject payload, log security event */ } throw $e; }

Prevention

When it happens

Trigger: unserialize() called on a crafted/malformed payload representing a Symfony Route where 'path', 'host' or 'condition' entries are objects implementing Stringable; unserializing user-supplied data that was doctored.

Common situations: Security-driven: apps that unserialize untrusted input containing Route-like payloads; payload tampering in caches or queues; passing random data to unserialize and hitting this guard.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of symfony/routing@83fa223250 (2026-09-14). Data as JSON: /api/errors/864ad52f90a693f3. Report an issue: GitHub.

Appendix: source

Thrown at Route.php:82

            'path' => $this->path,
            'host' => $this->host,
            'defaults' => $this->defaults,
            'requirements' => $this->requirements,
            'options' => $this->options,
            'schemes' => $this->schemes,
            'methods' => $this->methods,
            'condition' => $this->condition,
            'compiled' => $this->compiled,
        ];
    }

    public function __unserialize(array $data): void
    {
        if (($data['path'] ?? null) instanceof \Stringable
            || ($data['host'] ?? null) instanceof \Stringable
            || ($data['condition'] ?? null) instanceof \Stringable
        ) {
            throw new \BadMethodCallException('Cannot unserialize '.self::class);
        }

        $this->path = $data['path'];
        $this->host = $data['host'];
        $this->defaults = $data['defaults'];
        $this->requirements = $data['requirements'];
        $this->options = $data['options'];
        $this->schemes = $data['schemes'];
        $this->methods = $data['methods'];

        if (isset($data['condition'])) {
            $this->condition = $data['condition'];
        }
        if (isset($data['compiled'])) {
            $this->compiled = $data['compiled'];
        }
    }

View on GitHub (pinned to 83fa223250)