microsoft/aspire · error · ArgumentException

The apiPath must not be '/' — it would match all requests…

Error message

The apiPath must not be '/' — it would match all requests and make the static site unreachable.

What it means

An apiPath of '/' would make the proxy intercept every request to the static site, so the SPA assets would never be served. Aspire rejects it after normalizing (trailing slashes are trimmed, so '///' also collapses to empty) and requires a real sub-path prefix.

Solutions

  1. Use a distinct sub-path such as "/api" instead of "/".
  2. If everything must go to the backend, do not use the static-site proxy; point the resource at the backend directly or reverse-proxy at the infrastructure layer.
  3. Guard configurable values: if (string.IsNullOrEmpty(path.Trim('/'))) require a non-root default.

Example fix

// before
.WithApiProxy(apiPath: "/", apiTarget: apiBuilder);
// after
.WithApiProxy(apiPath: "/api", apiTarget: apiBuilder);
Defensive patterns

Strategy: validation

Validate before calling

if (string.IsNullOrEmpty(apiPath?.TrimEnd('/')))
    throw new ArgumentException("apiPath must be a non-root sub-path, not '/'.");

Prevention

When it happens

Trigger: Calling the API-proxy extension with apiPath: "/" (or a value that becomes empty after TrimEnd('/')), with a valid apiTarget.

Common situations: Attempting to proxy all traffic to a backend; a configurable prefix string that defaults to "/"; misunderstanding that trimming turns "/api/" into "/api" (fine) but "/" into empty (error).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/fabc6c27a0ec8e4f. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.JavaScript/JavaScriptHostingExtensions.cs:1310

        }

        if (apiTarget is not null && apiPath is null)
        {
            throw new ArgumentException("apiPath is required when apiTarget is specified.", nameof(apiPath));
        }

        if (apiPath is not null && apiTarget is not null)
        {
            if (!apiPath.StartsWith('/'))
            {
                throw new ArgumentException("The apiPath must start with '/'.", nameof(apiPath));
            }

            apiPath = apiPath.TrimEnd('/');

            if (apiPath.Length == 0)
            {
                throw new ArgumentException("The apiPath must not be '/' — it would match all requests and make the static site unreachable.", nameof(apiPath));
            }

            ValidateApiPath(apiPath);
            builder.WithReference(apiTarget);
        }

        if (!builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
        {
            return builder;
        }

        // YARP listens on port 5000 by default in the base image, so configure an endpoint for that port
        // and set ASPNETCORE_URLS to ensure Kestrel listens on the correct port as well for static file serving and API reverse-proxy to work correctly.
        builder.WithEndpoint("http", e => e.TargetPort = 5000, createIfNotExists: true);

        var annotation = new JavaScriptPublishModeAnnotation(JavaScriptPublishMode.StaticWebsite)
        {
            OutputPath = options.OutputPath,

View on GitHub (pinned to 25830f84bd)