Skip to content

Infinite redirect loop ("ERR_TOO_MANY_REDIRECTS") caused by encoded unsafe character ^ (%5e) in URL pathname #8041

Description

@markafox

Which project does this relate to?

Router

Describe the bug

This is the same issue as bug #7587 but happens with the ^ character. I have tried every other special character available on a standard US QWERTY keyboard and after the bug fix for 7587 this only still happens with the ^ character.

When a URL pathname contains encoded the unsafe character ^ (%5e) an infinite redirect loop is started and the browser crashes with an ERR_TOO_MANY_REDIRECTS error.

This happens in both basic paths and paths with parameters.

Unfortunately we use the ^ as a delimiter in a single route parameter.

Complete minimal reproducer

Any blank project initialized with @tanstack/cli is sufficient for reproducing the issue—no additional code is needed

Steps to Reproduce the Bug

  1. Create a TanStack Start project from scratch. You don't need to add anything; just start the dev server and proceed to step 2
  2. Open http://localhost:3000/^

Expected behavior

Should throw a 404 error if it is not part of a valid path or be passed as part of the path parameter if it is in a path parameter.

Screenshots or Videos

Image

Platform

  • Router / Start Version: Router v1.170.25 / React-Start v1.168.42
  • OS: Windows 11 - Ubuntu WSL2
  • Browser: Edge (chrominium)
  • Browser Version: v151.0.4129.72
  • Bundler: Vite
  • Bundler Version: v8.2.1

Additional context

No response

Activity

  1. spokodev commented on Sep 16, 2026

    @spokodev

    The cause, measured on current main:

    /a%5Eb   -> decodePath -> /a^b      -> encodePathLikeUrl -> /a^b      MISMATCH
    /a%7Cb   -> decodePath -> /a|b      -> encodePathLikeUrl -> /a|b      MISMATCH
    /%3Ctest -> decodePath -> /%3Ctest  -> encodePathLikeUrl -> /%3Ctest  ok
    

    decodeSegment uses decodeURI, which turns %5E into ^. PATH_UNSAFE_RE (/[\x00-\x1f\x7f"<>`{}]/g) then re-encodes only " < > { }, so ^is left literal and nothing puts it back - the encoded and re-encoded URLs differ and the redirect comparator sees a change.|` has the same problem and has not been reported.

    Adding ^ to PATH_UNSAFE_RE fixes %5E but breaks a literal ^, which browsers do send un-encoded, so it trades one mismatch for the other. The durable shape is to decode only what the encoder can reverse: encodePathLikeUrl re-encodes whitespace and non-ASCII, so having decodeSegment decode exactly those and leave every other %XX intact makes the pair exact in both directions.

    Happy to open a PR for that if the direction is right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions