reactiveui/refit · error · ArgumentException

URL path {relativePath} must start with '/' and be of the fo

Error message

URL path {relativePath} must start with '/' and be of the form '/foo/bar/baz'

What it means

Under the RefitLegacy URL resolution mode, any non-empty relative path on an HTTP attribute must begin with '/'. This guard enforces the legacy '/foo/bar' convention and also rejects CR/LF characters to prevent header/URL injection. Modern (non-legacy) mode is lenient about the leading slash.

Source

Thrown at src/Refit.Reflection/RestMethodInfoInternal.cs:320

    internal static PropertyInfo[] GetParameterProperties(ParameterInfo parameter) =>
        ReflectionPropertyHelpers.GetReadablePublicInstanceProperties(parameter.ParameterType);

    /// <summary>Verifies that the relative URL path is well formed and free of injection characters.</summary>
    /// <param name="relativePath">The relative URL path to validate.</param>
    /// <param name="urlResolution">The URL resolution mode; the leading-slash requirement is relaxed under <see cref="UrlResolutionMode.Rfc3986"/>.</param>
    /// <exception cref="ArgumentException"><paramref name="relativePath"/> contains a CR or LF character, or it does not
    /// start with '/' under <see cref="UrlResolutionMode.RefitLegacy"/>.</exception>
    internal static void VerifyUrlPathIsSane(string relativePath, UrlResolutionMode urlResolution)
    {
        if (string.IsNullOrEmpty(relativePath))
        {
            return;
        }

        if (urlResolution == UrlResolutionMode.RefitLegacy
            && !StringHelpers.StartsWith(relativePath, '/'))
        {
            throw new ArgumentException(
                $"URL path {relativePath} must start with '/' and be of the form '/foo/bar/baz'");
        }

        // CRLF injection protection
        if (!StringHelpers.ContainsCrOrLf(relativePath))
        {
            return;
        }

        throw new ArgumentException(
            $"URL path {relativePath} must not contain CR or LF characters");
    }

    /// <summary>Adds headers from a <see cref="HeadersAttribute"/> into the accumulated map.</summary>
    /// <param name="headersAttribute">The header attribute to process.</param>
    /// <param name="ret">The accumulated map, created as needed.</param>
    internal static void AddHeaders(HeadersAttribute headersAttribute, ref Dictionary<string, string?>? ret)
    {

View on GitHub (pinned to b455f65ecc)

Solutions

  1. Prefix every relative path with '/', e.g. [Get("/users/{id}")].
  2. If you rely on paths without a leading slash, switch UrlResolutionMode to the non-legacy mode in RefitSettings.
  3. Ensure no CR/LF characters are interpolated into path templates (sanitize any data used in {path} parameters).

Example fix

// before
[Get("users/{id}")] Task<User> GetAsync(string id); // RefitLegacy -> throws

// after
[Get("/users/{id}")] Task<User> GetAsync(string id);
Defensive patterns

Strategy: validation

Validate before calling

static void AssertPathStartsWithSlash(string path, UrlResolutionMode mode) {
    if (mode == UrlResolutionMode.RefitLegacy && !string.IsNullOrEmpty(path) && !path.StartsWith("/"))
        throw new InvalidOperationException($"Path '{path}' must start with '/' in RefitLegacy mode.");
    if (path.IndexOfAny(new[] { '\r', '\n' }) >= 0)
        throw new InvalidOperationException("Path must not contain CR/LF.");
}

Prevention

When it happens

Trigger: A [Get("users/{id}")] style attribute (no leading slash) is used while RefitSettings.UrlResolution is set to UrlResolutionMode.RefitLegacy; or a path contains a CR/LF character in any mode.

Common situations: Upgrading from old Refit where paths without '/' were silently accepted; switching UrlResolutionMode; templated path that accidentally includes a newline (injection or bad data).

Related errors


AI-assisted analysis of reactiveui/refit@b455f65ecc (2026-08-13). Data as JSON: /api/errors/f87f2dfe366872bc. Report an issue: GitHub.