Skip to content

AbortController is not properly caught in parent loader #3928

@brmzkw

Description

@brmzkw

Which project does this relate to?

Router

Describe the bug

First of all, I'm sorry if the title of the issue does not describe what is happening.

I have two routes:

  • the __root route with a loader performing a call to fetch to a slow API
  • an index route with a loader also performing a call to fetch to a slow API

Both calls forward the abortController.signal to fetch to ensure the request is canceled if necessary.

The index route is displaying a text input, and whenever the user types something, navigate is used which triggers a route reload.

If too many reloads are run, the error message "signal is aborted without reason" is triggered from the root view.

Your Example Website or App

https://stackblitz.com/edit/tanstack-router-juizcyv2?file=src%2Froutes%2Findex.tsx

Steps to Reproduce the Bug or Issue

  1. write one character at a time in the text input: it works well
  2. write many characters simultaneously: an error is triggered

Expected behavior

The abort error should be caught.

Screenshots or Videos

Screen.Recording.2025-04-03.at.13.41.43.mov

Platform

Additional context

root.tsx:

import * as React from 'react';
import { Link, Outlet, createRootRoute } from '@tanstack/react-router';
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools';

export const Route = createRootRoute({
  component: RootComponent,

  loader: async ({ abortController }) => {
    try {
      const data = await fetch('https://httpstat.us/200?sleep=1000', {
        signal: abortController.signal,
      });
    } catch (e) {
      console.log('in __root, aborted');
      throw e;
    }
  },
});

function RootComponent() {
  return (
    <>
      <div className="p-2 flex gap-2 text-lg">
        <Link
          to="/"
          activeProps={{
            className: 'font-bold',
          }}
          activeOptions={{ exact: true }}
        >
          Home
        </Link>{' '}
        <Link
          to="/about"
          activeProps={{
            className: 'font-bold',
          }}
        >
          About
        </Link>
      </div>
      <hr />
      <Outlet />
      <TanStackRouterDevtools position="bottom-right" />
    </>
  );
}

index.tsx:

import * as React from 'react';
import { createFileRoute } from '@tanstack/react-router';

type Search = {
  filter: string;
};

export const Route = createFileRoute('/')({
  component: HomeComponent,

  validateSearch: (search: Record<string, unknown>): Search => {
    return {
      filter: (search.filter as string) || '',
    };
  },

  loaderDeps: ({ search }) => {
    return {
      filter: search.filter,
    };
  },

  loader: async ({ deps, abortController }) => {
    console.log('loading', deps.filter);
    try {
      const data = await fetch('https://httpstat.us/200?sleep=1000', {
        signal: abortController.signal,
      });
    } catch (e) {
      console.log('in index, signal aborted for', deps.filter);
      throw e;
    }

    console.log('loaded:', deps.filter);
    return { status: 'loaded: ' + deps.filter };
  },
});

function HomeComponent() {
  const search = Route.useSearch();
  const navigate = Route.useNavigate();
  const data = Route.useLoaderData();

  return (
    <div className="p-2">
      <h3>Welcome Home!</h3>

      <h4>Status: {data.status}</h4>

      <input
        className="bg-gray-300 border border-black"
        value={search.filter}
        onChange={(e) => {
          navigate({
            to: '/',
            search: {
              filter: e.target.value,
            },
          });
        }}
      />
    </div>
  );
}

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