This error occurs when React Query receives an object directly as the queryKey instead of the required array format. While query keys can contain objects as elements within the array, the queryKey itself must always be an array. Understanding the correct queryKey structure prevents cache mismatches and query function errors.
React Query (TanStack Query) uses query keys to uniquely identify and cache queries. The queryKey parameter must be an array at the top level - this is a strict requirement of the library. When you pass an object directly instead of wrapping it in an array, React Query cannot properly process the key for caching, invalidation, or refetching. The library expects the format `queryKey: [...]` where the array can contain strings, numbers, and objects as elements. This error specifically occurs when you accidentally pass `queryKey: {...}` (a plain object) instead of `queryKey: [{...}]` (an array containing an object). The array structure allows React Query to perform hierarchical matching when invalidating queries and ensures consistent key serialization across your application.
Locate the useQuery or useMutation call that is causing the error. Ensure the queryKey is an array. If you have an object with query parameters, wrap it in an array.
// WRONG - passing object directly
const { data } = useQuery({
queryKey: { userId: 1, filter: "active" },
queryFn: fetchUser
});
// CORRECT - object wrapped in array
const { data } = useQuery({
queryKey: [{ userId: 1, filter: "active" }],
queryFn: fetchUser
});
// BEST PRACTICE - use a descriptive string first
const { data } = useQuery({
queryKey: ["user", { userId: 1, filter: "active" }],
queryFn: fetchUser
});Follow React Query best practices by starting with a descriptive string identifier, then adding variables. The array format allows React Query to match query keys hierarchically for invalidation.
// Simple query with no parameters
const { data: todos } = useQuery({
queryKey: ["todos"],
queryFn: fetchTodos
});
// Query with a single parameter
const { data: todo } = useQuery({
queryKey: ["todo", todoId],
queryFn: () => fetchTodo(todoId)
});
// Query with multiple parameters
const { data: filteredTodos } = useQuery({
queryKey: ["todos", { status: "done", page: 1 }],
queryFn: () => fetchTodos({ status: "done", page: 1 })
});A common mistake is misspelling "queryKey". React Query will not recognize the property and may default to undefined or throw a type error.
// WRONG - typo in property name
const { data } = useQuery({
gueryKey: ["todos"], // typo: "gueryKey" instead of "queryKey"
queryFn: fetchTodos
});
// WRONG - incorrect casing
const { data } = useQuery({
querykey: ["todos"], // should be "queryKey" with capital K
queryFn: fetchTodos
});
// CORRECT
const { data } = useQuery({
queryKey: ["todos"],
queryFn: fetchTodos
});If you are migrating from an older version of React Query, you may have string query keys that need to be converted to arrays. React Query v3 and later require array format.
// OLD (React Query v2)
const { data } = useQuery("todos", fetchTodos);
// NEW (React Query v3+)
const { data } = useQuery({
queryKey: ["todos"],
queryFn: fetchTodos
});If you use a function or factory to generate query keys dynamically, ensure it returns an array, not an object.
// WRONG - function returns object
const getUserKey = (userId: number) => ({ userId });
const { data } = useQuery({
queryKey: getUserKey(1), // returns object, not array
queryFn: () => fetchUser(1)
});
// CORRECT - function returns array
const getUserKey = (userId: number) => ["user", userId];
const { data } = useQuery({
queryKey: getUserKey(1), // returns ["user", 1]
queryFn: () => fetchUser(1)
});
// BETTER - function returns array with object
const getUserKey = (userId: number, options?: object) =>
["user", { userId, ...options }];
const { data } = useQuery({
queryKey: getUserKey(1, { includeProfile: true }),
queryFn: () => fetchUser(1)
});If the error occurs during query invalidation or when calling queryClient methods, ensure you pass array keys there as well.
// WRONG - passing object to invalidateQueries
queryClient.invalidateQueries({ userId: 1 });
// CORRECT - passing array with object
queryClient.invalidateQueries({ queryKey: ["user", { userId: 1 }] });
// CORRECT - invalidating all queries matching prefix
queryClient.invalidateQueries({ queryKey: ["user"] });If you are using TypeScript, ensure you have strict type checking enabled. React Query types will prevent you from passing incorrect queryKey formats.
// In tsconfig.json
{
"compilerOptions": {
"strict": true
}
}With strict mode enabled, TypeScript will show an error if you try to pass a non-array queryKey:
// TypeScript will error before runtime
const { data } = useQuery({
queryKey: { userId: 1 }, // Type error: object is not assignable to QueryKey
queryFn: fetchUser
});React Query uses query keys for more than just identification - they are the foundation of its caching and invalidation system. The array structure allows hierarchical matching, meaning queryClient.invalidateQueries({ queryKey: ["todos"] }) will match all keys starting with "todos", such as ["todos"], ["todos", 1], and ["todos", { status: "done" }]. Object property order within array elements does not matter for equality - ["todos", { status, page }] and ["todos", { page, status }] are considered identical. However, array element order does matter - ["todos", status, page] and ["todos", page, status] are different keys. For complex applications, consider creating query key factories that encapsulate key generation logic and ensure consistency. For example, const todoKeys = { all: ["todos"] as const, lists: () => [...todoKeys.all, "list"] as const, list: (filters: string) => [...todoKeys.lists(), { filters }] as const }. This pattern prevents typos and makes refactoring easier. Always include any variables your query function depends on in the queryKey array - this ensures queries refetch automatically when dependencies change.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite