slimphp/Slim · error · InvalidArgumentException

Missing data for URL segment: %s

Error message

Missing data for URL segment: %s

What it means

RouteParser::relativeUrlFor() (used by urlFor() and fullUrlFor()) expands the route pattern into URL segments, filling each {placeholder} from the $data array you pass. It tries every expression variant from most specific (all optional params filled) to least; if no variant can be satisfied — i.e. some required placeholder has no entry in $data — it throws this InvalidArgumentException, naming the first placeholder it could not resolve. The lookup uses array_key_exists, so keys must match placeholder names exactly (case-sensitive); numeric-indexed arrays from older idioms do not work.

Source

Thrown at Slim/Routing/RouteParser.php:90

                    $segmentName = $segment[0];
                    break;
                }

                $segments[] = $data[$segment[0]];
            }

            /*
             * If we get to this logic block we have found all the parameters
             * for the provided $data which means we don't need to continue testing
             * less specific expressions
             */
            if (!empty($segments)) {
                break;
            }
        }

        if (empty($segments)) {
            throw new InvalidArgumentException('Missing data for URL segment: ' . $segmentName);
        }

        $url = implode('', $segments);
        if ($queryParams) {
            $url .= '?' . http_build_query($queryParams);
        }

        return $url;
    }

    /**
     * {@inheritdoc}
     */
    public function urlFor(string $routeName, array $data = [], array $queryParams = []): string
    {
        $basePath = $this->routeCollector->getBasePath();
        $url = $this->relativeUrlFor($routeName, $data, $queryParams);

View on GitHub (pinned to 80900fb39c)

Solutions

  1. Supply a value for every required placeholder, keyed exactly by its name: urlFor('user.post', ['id' => 1, 'postId' => 9])
  2. Check the data keys against the route pattern for typos and case mismatches — placeholder names are case-sensitive
  3. If the segment is legitimately omittable, make it optional in the pattern: /user/{id}[/{slug}]
  4. Add a pre-flight check that diffs array keys against required placeholders parsed from the pattern before calling urlFor()

Example fix

// before
$app->get('/user/{id}/posts/{postId}', PostAction::class)->setName('user.post');
$url = $routeParser->urlFor('user.post', ['id' => 1]); // throws 'Missing data for URL segment: postId'

// after
$url = $routeParser->urlFor('user.post', ['id' => 1, 'postId' => 9]);
Defensive patterns

Strategy: validation

Validate before calling

function requiredRouteParams(string $pattern): array
{
    // strip optional segments (square brackets, innermost first)
    while (preg_match('/\[[^\[\]]*\]/', $pattern)) {
        $pattern = (string) preg_replace('/\[[^\[\]]*\]/', '', $pattern);
    }
    preg_match_all('/\{([^}:]+)(?::[^}]*)?\}/', $pattern, $m);
    return $m[1];
}

$route = $collector->getNamedRoute($name);
$missing = array_diff(requiredRouteParams($route->getPattern()), array_keys($data));
if ($missing) {
    throw new InvalidArgumentException(
        sprintf('urlFor(%s) is missing: %s', $name, implode(', ', $missing))
    );
}
$url = $routeParser->urlFor($name, $data);

Try / catch

try {
    $url = $routeParser->urlFor($name, $data);
} catch (InvalidArgumentException $e) {
    // message names the missing placeholder — log it and fall back to a safe default
    $log->warning($e->getMessage(), ['route' => $name, 'data' => $data]);
    $url = $defaultPath ?? '/';
}

Prevention

When it happens

Trigger: Calling $routeParser->urlFor('user.post', ['id' => 1]) for a route pattern like /user/{id}/posts/{postId} — the message will name 'postId'; a typo'd or wrongly-cased data key ('userId' vs 'id'); passing an empty array for a route with any required placeholder; forgetting that only placeholders inside [...] brackets are optional and can be omitted.

Common situations: Adding a new required segment to a route pattern but not updating every urlFor() call site; mixed conventions where some code passes positional arrays; template or config files storing route data keyed by old names; optional segments like /archive[/{year}] working without data while /user/{id} requiring it, causing inconsistent expectations across routes.

Related errors


AI-assisted analysis of slimphp/Slim@80900fb39c (2026-08-21). Data as JSON: /api/errors/37a557c24b1d8c5f. Report an issue: GitHub.