How to fix fetch did not throw or return a rejected promise. You must handle error throwing in your query function in React

ReactINTERMEDIATEMEDIUMUpdated Dec 10, 2025ยท 142 views

React Query cannot detect errors from fetch API calls because fetch does not automatically throw errors on HTTP error status codes. You must manually check response.ok and throw errors in your queryFn.

Quick fix

Fixes fetch did not throw or return a rejected promise. You must handle error throwing in your query function

const { data, isError, error } = useQuery({
  queryKey: ['user', userId],
  queryFn: async () => {
    const response = await fetch(`/api/users/${userId}`);

    if (!response.ok) {
      throw new Error('Network response was not ok');
    }

    return response.json();
  }
});
  1. Check response.ok and throw errors
  2. Include detailed error information
  3. Create a reusable fetch wrapper
  4. Handle error response bodies
react โ€” error
const { data, isError, error } = useQuery({
queryKey: ['user', userId],
queryFn: async () => {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
}
});
fetch did not throw or return a rejected promise. You must handle error throwing in your query function

What this error means

This error occurs when using TanStack Query (React Query) with the native fetch API. Unlike libraries like axios or graphql-request that automatically throw errors for unsuccessful HTTP calls, the fetch API does not throw errors by default for HTTP error status codes (4xx, 5xx). React Query requires query functions to either throw an error or return a rejected Promise to signal that something went wrong. When fetch returns a response with an error status code (like 404 or 500), it still resolves the Promise successfully with a Response object. This means React Query cannot detect the error and properly update the query state. The error message indicates that your queryFn completed successfully without throwing or rejecting, even though the HTTP request failed. This prevents React Query from triggering its error handling mechanisms and updating the error state properly.

Typical symptoms

  • React Query shows success state even when API returns error status codes
  • isError remains false despite failed HTTP requests
  • error property is undefined even after receiving 4xx or 5xx responses
  • Query appears to succeed but returns error response bodies
  • Error boundaries do not catch failed API calls
  • onError callbacks never trigger for failed requests

Common causes

  1. Using fetch API without checking response.ok property
  2. Not throwing errors in queryFn when HTTP status indicates failure
  3. Returning response directly without validation
  4. Missing error handling in async query functions
  5. Catching errors without re-throwing them in queryFn

How to fix "fetch did not throw or return a rejected promise. You must handle error throwing in your query function"

1Check response.ok and throw errors

The most common solution is to check the response.ok property and throw an error if it's false:

typescript
const { data, isError, error } = useQuery({
  queryKey: ['user', userId],
  queryFn: async () => {
    const response = await fetch(`/api/users/${userId}`);

    if (!response.ok) {
      throw new Error('Network response was not ok');
    }

    return response.json();
  }
});

This ensures React Query can properly detect and handle errors. The response.ok property is true for status codes in the 200-299 range.

2Include detailed error information

For better error handling, include the status code and status text in your error:

typescript
const { data, isError, error } = useQuery({
  queryKey: ['user', userId],
  queryFn: async () => {
    const response = await fetch(`/api/users/${userId}`);

    if (!response.ok) {
      throw new Error(
        `HTTP error! status: ${response.status} ${response.statusText}`
      );
    }

    return response.json();
  }
});

This provides more context when debugging errors.

3Create a reusable fetch wrapper

To avoid repeating error handling logic, create a wrapper function:

typescript
async function fetchWithErrorHandling(url: string, options?: RequestInit) {
  const response = await fetch(url, options);

  if (!response.ok) {
    const errorBody = await response.text();
    throw new Error(
      `HTTP ${response.status}: ${errorBody || response.statusText}`
    );
  }

  return response.json();
}

// Use in queries
const { data } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchWithErrorHandling(`/api/users/${userId}`)
});

This centralizes error handling and makes it reusable across all queries.

4Handle error response bodies

If your API returns error details in the response body, extract them:

typescript
const { data, error } = useQuery({
  queryKey: ['user', userId],
  queryFn: async () => {
    const response = await fetch(`/api/users/${userId}`);

    if (!response.ok) {
      const errorData = await response.json().catch(() => null);
      throw new Error(
        errorData?.message || `HTTP error ${response.status}`
      );
    }

    return response.json();
  }
});

// Access error message
if (isError) {
  console.log(error.message); // Shows API error message
}

This allows you to display server-provided error messages to users.

5Use Promise.reject() as alternative

Instead of throwing, you can return a rejected Promise:

typescript
const { data, isError } = useQuery({
  queryKey: ['user', userId],
  queryFn: async () => {
    const response = await fetch(`/api/users/${userId}`);

    if (!response.ok) {
      return Promise.reject({
        status: response.status,
        message: response.statusText
      });
    }

    return response.json();
  }
});

Both throw and Promise.reject() work equally well for signaling errors to React Query.

Advanced notes

Re-throwing Caught Errors

If you catch errors during processing, you must re-throw them for React Query to detect them:

typescript
queryFn: async () => {
  try {
    const response = await fetch('/api/data');
    if (!response.ok) throw new Error('Fetch failed');
    return response.json();
  } catch (error) {
    // Log for debugging
    console.error('Query failed:', error);
    // Re-throw so React Query sees it
    throw error;
  }
}

Custom Error Objects

You can throw custom error objects with additional data:

typescript
class ApiError extends Error {
  constructor(
    public status: number,
    public statusText: string,
    public data?: any
  ) {
    super(`API Error: ${status} ${statusText}`);
    this.name = 'ApiError';
  }
}

queryFn: async () => {
  const response = await fetch('/api/data');
  if (!response.ok) {
    const data = await response.json().catch(() => null);
    throw new ApiError(response.status, response.statusText, data);
  }
  return response.json();
}

Global Error Handling

Configure default error handling for all queries using QueryClient:

typescript
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: (failureCount, error) => {
        // Don't retry on 4xx errors
        if (error instanceof ApiError && error.status < 500) {
          return false;
        }
        return failureCount < 3;
      },
      onError: (error) => {
        // Global error handler
        console.error('Query error:', error);
        toast.error('Something went wrong');
      }
    }
  }
});

Comparison with Axios

Axios automatically throws for error status codes, so this pattern isn't needed:

typescript
// With axios - no manual error checking needed
const { data } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => axios.get(`/api/users/${userId}`).then(res => res.data)
});

Consider using axios or a similar library if you want automatic error handling across your application.

Frequently asked questions

What does "fetch did not throw or return a rejected promise. You must handle erro" mean?
This error occurs when using TanStack Query (React Query) with the native fetch API. Unlike libraries like axios or graphql-request that automatically throw errors for unsuccessful HTTP calls, the fetch API does not throw errors by default for HTTP error status codes (4xx, 5xx). React Query requires query functions to either throw an error or return a rejected Promise to signal that something went wrong.
How do I fix "fetch did not throw or return a rejected promise. You must handle erro"?
React Query cannot detect errors from fetch API calls because fetch does not automatically throw errors on HTTP error status codes. You must manually check response.ok and throw errors in your queryFn.
What causes "fetch did not throw or return a rejected promise. You must handle erro"?
Using fetch API without checking response.ok property Not throwing errors in queryFn when HTTP status indicates failure Returning response directly without validation
Is the "fetch did not throw or return a rejected promise. You must handle erro" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading