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

RequireLeadingSlashUnderLegacy rejects a non-empty relative path that does not start with '/', mirroring the legacy reflection request builder's behavior. Under legacy resolution a leading slash is required so the base path concatenation yields a well-formed absolute path; otherwise an ArgumentException is thrown.

Source

Thrown at src/Refit/GeneratedRequestRunner.cs:711

            return path;
        }

        var key = path[(i + 1)..j];
        throw new ArgumentException(
            $"URL {relativePathTemplate} has parameter {{{key}}}, but no method parameter matches");
    }

    /// <summary>Rejects a no-leading-slash path under legacy resolution, matching the reflection request builder.</summary>
    /// <param name="relativePath">The resolved relative request path.</param>
    /// <exception cref="ArgumentException">The path is non-empty and does not start with '/'.</exception>
    internal static void RequireLeadingSlashUnderLegacy(string relativePath)
    {
        if (relativePath.Length == 0 || relativePath[0] == '/')
        {
            return;
        }

        throw new ArgumentException(
            $"URL path {relativePath} must start with '/' and be of the form '/foo/bar/baz'");
    }

    /// <summary>Builds the message describing an invalid <c>[Url]</c> parameter value.</summary>
    /// <param name="value">The rejected value.</param>
    /// <returns>The exception message.</returns>
    internal static string FormatAbsoluteUrlError(object? value) =>
        $"The [Url] parameter value \"{value}\" must be an absolute URI (for example \"https://host/path\").";

    /// <summary>Adds one pre-boxed configured request property or option value.</summary>
    /// <param name="request">The request to modify.</param>
    /// <param name="key">The property key.</param>
    /// <param name="value">The pre-boxed property value.</param>
#if NET6_0_OR_GREATER
    internal static void AddBoxedRequestProperty(HttpRequestMessage request, string key, object value) => request.Options.Set(new(key), value);
#else
    internal static void AddBoxedRequestProperty(HttpRequestMessage request, string key, object value) => request.Properties[key] = value;
#endif

View on GitHub (pinned to b455f65ecc)

Solutions

  1. Add a leading '/' to every non-empty route template: [Get("/things/42")]
  2. If building the path in code, prefix it with '/' before it reaches the builder
  3. Switch to UrlResolutionMode.Rfc3986 if the slash-less convention must be kept (it emits the path verbatim)
  4. Audit all interface route templates after changing the resolution mode to legacy

Example fix

// before
[Get("things/{id}")]
Task<Thing> GetAsync(long id); // legacy => throws, no leading slash

// after
[Get("/things/{id}")]
Task<Thing> GetAsync(long id);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure every non-empty route template starts with '/' under legacy resolution.
static string EnsureLeadingSlash(string route) =>
    string.IsNullOrEmpty(route) || route[0] == '/' ? route : "/" + route;

Type guard

static bool HasLeadingSlash(string route) =>
    route.Length == 0 || route[0] == '/';

Try / catch

try { await api.GetAsync(); }
catch (ArgumentException ex) when (ex.Message.Contains("must start with '/'"))
{
    // fix the route template to begin with '/' or switch to Rfc3986 resolution
}

Prevention

When it happens

Trigger: Under UrlResolutionMode.Legacy, a route template or computed relative path that is non-empty but lacks a leading '/', e.g. [Get("things/42")] instead of [Get("/things/42")], or a [Url]/path substitution that produces a bare segment.

Common situations: Copying a route from a framework that omits leading slashes; building a path dynamically and forgetting the '/'; switching resolution mode to legacy on an interface whose templates were authored for Rfc3986 (which is lenient).

Related errors


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