“Illegal characters in path.” only when viewing from mobile browser

I’m using a traditional WebAPI controller:

    public HttpResponseMessage GetQueryResults(string query)
            var userAgent = Request.Headers.UserAgent;
            var result = _fooService.GetResults(GetUsername(), query);
            var response = Request.CreateResponse(HttpStatusCode.OK, result);
            return response;

GetResults returns an array of elements that looks like this:

On most browsers, this works fine. My user agent header looks like this:
{Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36}

However when I debug from Chrome using the device toolbar, or when I visit my site from Safari on my iPhone, my user agent changes. From Chrome’s device toolbar (the mobile simulator), it looks something like this:
{Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Mobile Safari/537.36}

And in this case, CheckInvalidPathChars is invoked and fails against the JSON:

quot;type”: “System.ArgumentException”,
↵ “message”: “Illegal characters in path.”,
↵ “stackTrace”: ” at
System.IO.Path.CheckInvalidPathChars(String path, Boolean checkAdditional)rn at System.IO.Path.GetExtension(String path)rn
at System.Web.WebPages.DefaultDisplayMode.TransformPath(String
virtualPath, String suffix)rn at
httpContext, String virtualPath, Func2 virtualPathExists)rn at System.Web.WebPages.DisplayModeProvider.GetDisplayInfoForVirtualPath(String virtualPath, HttpContextBase httpContext, Func2 virtualPathExists,
IDisplayMode currentDisplayMode, Boolean
requireConsistentDisplayMode)rn at
System.Web.WebPages.WebPageRoute.GetRouteLevelMatch(String pathValue,
String[] supportedExtensions, Func2 virtualPathExists, HttpContextBase context, DisplayModeProvider displayModeProvider)rn at System.Web.WebPages.WebPageRoute.MatchRequest(String pathValue, String[] supportedExtensions, Func2 virtualPathExists,
HttpContextBase context, DisplayModeProvider displayModes)rn at
context)rn at
sender, EventArgs e)rn at
at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)rn
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step,
Boolean& completedSynchronously)”

I can reproduce this manually by attempting to parse the serialized JSON as a file path:

            var isValid = System.IO.Path.GetExtension(jsonString);
        catch (Exception e)
            throw e;

But of course, attempting to parse a serialized JSON object as a file will throw errors. Why is ASP.NET modifying parsing behavior based on user agent headers?

Can I somehow override the inbound header from the Request to coerce the framework towards functional behavior?

To be clear – I can invoke the controller from Chrome (standard) with no issues. When I invoke the controller with the same exact request from Chrome (with devtools open and the mobile simulator active), the exception is thrown. Likewise, when I invoke it with the same exact request from Safari on iPhone, the exception is thrown. The User Agent header is the independent variable in these cases – so it must follow that this header is somehow causing a different execution path to be invoked. Right?

The exception is thrown after all Controller logic has executed – the response is returned by the controller when it’s thrown.


Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

As @Dipen Shah pointed out, the solution is in another post.

Why is ASP.NET modifying parsing behavior based on user agent headers?

There is a WebPageHttpModule registered automatically in your ASP.NET application (reference). Internally, WebPageHttpModule tries to route request based on display mode.

// NOTE: Excluded unnecessary code
public sealed class DisplayModeProvider
    public static readonly string MobileDisplayModeId = "Mobile";

    private readonly List<IDisplayMode> _displayModes = new List<IDisplayMode>()
        (IDisplayMode) new DefaultDisplayMode(DisplayModeProvider.MobileDisplayModeId)
            ContextCondition = (Func<HttpContextBase, bool>) (context => context.GetOverriddenBrowser().IsMobileDevice)
        (IDisplayMode) new DefaultDisplayMode()

    public IList<IDisplayMode> Modes
            return (IList<IDisplayMode>) this._displayModes;

You can see string Mobile there and check condition against IsMobileDevice (reference). That’s why the solution in aforementioned post makes sense. Once you know how it works internally, you can do the following hack as well.
public static void RegisterDisplayModes()
    var displayModes = DisplayModeProvider.Instance.Modes;
    var mobileDisplayMode = displayModes.FirstOrDefault(d => d.DisplayModeId == DisplayModeProvider.MobileDisplayModeId);
    if (mobileDisplayMode != null)

Note, this is based on decompiled System.Web.WebPages.dll located at C:Program Files (x86)Microsoft ASP.NETASP.NET Web Pagesv2.0Assemblies.

Method 2

I used Asp.net Core kindly find the code below and screenshot assuming i understood your question perfectly.

    public IActionResult GetQueryResults(string query)
        var userAgent = Request.Headers["User-Agent"].ToString();
        var result = @"[{'resultId':2039016,'text':null,'dateCreated':'2020-09-10T02:24:36.003','targetPlatform':'FooBar'}]";
        // _fooService.GetResults(GetUsername(), query);
        return Ok(new { result = result, userAgent = userAgent });
  • This is the image screenshot from vscode

“Illegal characters in path.” only when viewing from mobile browser

  • This is the screenshot using chrome mobile responsive

“Illegal characters in path.” only when viewing from mobile browser

  • This is the screenshot using chrome web or desktop view

“Illegal characters in path.” only when viewing from mobile browser

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Notify of

Inline Feedbacks
View all comments
Would love your thoughts, please comment.x