flarum/framework · error · RuntimeException

Route $name already exists

Error message

Route $name already exists

What it means

Flarum's RouteCollection stores routes keyed by unique name. addRoute() throws this RuntimeException when you try to register a route whose $name is already taken, because duplicate names would make reverse URL generation (getPath) ambiguous. It is an intentional registration-time guard, not a runtime failure.

Solutions

  1. Rename one of the routes so each name is unique across the application.
  2. Call removeRoute($name) before re-registering if overwriting is intended.
  3. Check which extension(s) register the colliding name (search for the route name string in registered extensions).
  4. Ensure route registration code runs only once (e.g. not inside a loop or a repeatedly-invoked boot hook).

Example fix

// before
$routes->get('/forum', 'index', IndexHandler::class);
$routes->get('/', 'index', HomeHandler::class); // RuntimeException
// after
$routes->get('/forum', 'forum.index', IndexHandler::class);
$routes->get('/', 'home.index', HomeHandler::class);
Defensive patterns

Strategy: validation

Validate before calling

if (array_key_exists($name, $routes->getRoutes())) {
    $routes->removeRoute($name); // or skip/throw your own clearer error
}
$routes->get($path, $name, $handler);

Type guard

function routeNameIsFree(RouteCollection $routes, string $name): bool {
    return !array_key_exists($name, $routes->getRoutes());
}

Try / catch

try {
    $routes->get($path, $name, $handler);
} catch (\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'already exists')) {
        $routes->removeRoute($name)->get($path, $name, $handler);
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Calling addRoute(), get(), post(), put(), patch() or delete() twice with the same route name on the same RouteCollection — e.g. two extensions registering a route named 'index', or re-running a route registration closure on every request without a fresh collection.

Common situations: Two extensions both registering a default frontend route with the same name; copy-pasted route registration code where the second call forgot to change the name; boot code executed twice due to a service provider being registered multiple times.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of flarum/framework@4b939f6853 (2026-09-15). Data as JSON: /api/errors/a7dee0d6c0891683. Report an issue: GitHub.

Appendix: source

Thrown at framework/core/src/Http/RouteCollection.php:61

    public function put(string $path, string $name, callable|string $handler): self
    {
        return $this->addRoute('PUT', $path, $name, $handler);
    }

    public function patch(string $path, string $name, callable|string $handler): self
    {
        return $this->addRoute('PATCH', $path, $name, $handler);
    }

    public function delete(string $path, string $name, callable|string $handler): self
    {
        return $this->addRoute('DELETE', $path, $name, $handler);
    }

    public function addRoute(string $method, string $path, string $name, callable|string $handler): self
    {
        if (isset($this->routes[$name])) {
            throw new \RuntimeException("Route $name already exists");
        }

        $this->routes[$name] = $this->pendingRoutes[$name] = compact('method', 'path', 'handler');

        return $this;
    }

    public function removeRoute(string $name): self
    {
        unset($this->routes[$name], $this->pendingRoutes[$name]);

        return $this;
    }

    protected function applyRoutes(): void
    {
        foreach ($this->pendingRoutes as $name => $route) {
            $routeDatas = $this->routeParser->parse($route['path']);

View on GitHub (pinned to 4b939f6853)